Lambda 在无穷无尽的流中使用整数捕获闭包抛出 OutOfMemoryError

Posted

技术标签:

【中文标题】Lambda 在无穷无尽的流中使用整数捕获闭包抛出 OutOfMemoryError【英文标题】:Lambda capturing closure with Integer in endless stream throws OutOfMemoryError 【发布时间】:2021-12-28 16:36:09 【问题描述】:

低于 100% 的代码抛出 java.lang.OutOfMemoryError

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);
                        
                    
            );

AFAIK 这是因为 lambda 捕获带有上下文的整数?谁能解释一下这里到底发生了什么?

【问题讨论】:

你的java版本是多少?我有 openjdk 版本 "11.0.12" 2021-07-20 OpenJDK 运行时环境 Homebrew (build 11.0.12+0) OpenJDK 64-Bit Server VM Homebrew (build 11.0.12+0, 混合模式)。当我使用-Xmx128m 运行此代码时,堆永远不会超过 90mb 对不起,我编辑了代码示例,请尝试 【参考方案1】:

在您的代码中,您有 Set,可通过 main 方法访问。本地栈是 GC 根,所以不能被 GC 收集。

因此,您正在向集合中添加无法收集的元素。并且 set 需要额外的内存来存储元素。

在我的 PC 上,在没有 OOM 的情况下运行这个程序需要大约 600 MB 的堆空间。

这是在我的电脑上运行的这个程序的堆转储。

我已经用 vanilla for 循环尝试了相同的代码并得到了相同的结果。

所以您只需要为您的应用程序添加更多内存。例如-Xmx1g 会将您的堆大小设置为 1 GB。

【讨论】:

其实我是从单元测试开始的,和main方法一样吗? 是的,从单元测试开始时,您的代码也可以从本地堆栈访问。你可以在你的surefire插件的配置中设置内存设置***.com/a/7579853/4655217

以上是关于Lambda 在无穷无尽的流中使用整数捕获闭包抛出 OutOfMemoryError的主要内容,如果未能解决你的问题,请参考以下文章

从使用 fmemopen 创建的流中读取宽字符

Dart:让流中抛出的异常传播并在调用者中捕获它

在 C++11 中按值或引用使用 lambda 默认捕获的缺点?

从低内存消耗的流中提取二进制值

C++ lambda表达式(函数指针和function)

Java 8 流中的 LINQ Enumerable.Single() [重复]