捕获Integer的Lambda闭包在无限流中抛出OutOfMemoryError原因求解
错误原因说明
你的推测不成立,该OOM异常和Lambda上下文捕获没有任何关联,核心原因是HashSet存储近1000万个Integer对象的总内存占用超过了JVM的堆内存上限。
你提供的代码如下:
Set<Integer> set = new HashSet<>(); new Random().ints(10_000_000, Integer.MIN_VALUE, Integer.MAX_VALUE) .forEach( v -> { if(set.contains(v)){ System.out.println(v); }else{ set.add(v); } } );
具体发生逻辑
- 代码中
Random.ints(10_000_000, Integer.MIN_VALUE, Integer.MAX_VALUE)会生成包含1000万个随机int值的有限流,由于int的取值范围为-231到231-1(共约42亿个可能值),1000万个随机值的重复概率极低,几乎所有值都会被存入HashSet中。 - HashSet底层基于HashMap实现,每个存入的元素会对应两个实例对象:1个
Integer装箱对象、1个HashMap的Node节点对象。以64位JVM默认开启指针压缩的场景计算:- 单个Integer对象占16字节(12字节对象头 + 4字节int值,刚好对齐到8字节的内存分配规则)
- 单个HashMap Node节点占32字节(12字节对象头 + 4字节hash值 + 3个各4字节的引用(key、value、next节点),对齐到8的倍数后为32字节)
- HashMap默认负载因子为0.75,当存入1000万个元素时,底层数组容量会扩容到约1600万,数组本身占用约64MB(每个数组元素是4字节的引用)
- 总内存占用约为540MB,如果是未开启指针压缩的64位JVM,总占用会超过800MB
- 多数运行环境下JVM默认的最大堆内存小于上述值(比如客户端模式JVM默认堆上限仅为256MB或512MB),因此运行时100%会抛出
java.lang.OutOfMemoryError: Java heap space异常。
Lambda捕获逻辑的误区澄清
你提到的Lambda捕获Integer的猜测完全不成立:
- 该Lambda表达式仅捕获了外部的
set变量,是Set实例的引用,仅占4/8字节,不会产生额外的内存堆积 - Lambda入参
v是流传递的局部参数,不属于上下文捕获的变量,每处理完一个元素就会被回收,不会导致内存溢出
内容的提问来源于stack exchange,提问作者J.J. Beam
相关产品推荐
相关产品推荐

