在Java中生成应用程序时,我在使用Stream.map修剪列表中的所有元素时遇到了垃圾回收的一些问题。匿名lambda类的实例存在于堆转储中,即使封闭类的实例为0,如可视VM的快照所示。

Java Lambdas堆转储-Lambda实例未获得垃圾收集-LMLPHP

LambdaTesting类:

class LambdaTesting {

    protected List<String> values;

    protected LambdaTesting(List<String> values) {
        this.values = values;
    }
    public List<String> modify() {
        return this.values.stream().map(x -> x.trim()).collect(Collectors.toList());
    }
    public List<String> modifyLocal() {
        List<String> localValue = new ArrayList<>();
        localValue.add("Local FOO ");
        localValue.add("Local BAR ");
        return localValue.stream().map(x -> x.trim()).collect(Collectors.toList());
   }
}

创建LambdaTesting实例并调用以下方法的方法:
public List<String> testMethods() {
    List<String> test = new ArrayList<>();
    test.add("Global FOO  ");
    test.add("   GLOBAL BAR");
    LambdaTesting lambdaTesting = new LambdaTesting(test);
    lambdaTesting.modifyLocal();
    lambdaTesting.modify();
}

在调用testMethods之后,将调试点放在下一行之后进行线程转储。

为什么对Lambda的引用仍然存在于堆转储中?

最佳答案

Does a lambda expression create an object on the heap every time it's executed?所述,一个非捕获的lambda表达式将被记住并重新使用,这意味着它与创建它的代码永久关联。这与例如只要包含文字的代码仍然存在,其对象表示形式就会保留在内存中的字符串文字。

这是一个实现细节。不一定要这样,但是引用实现以及所有常用的JRE都可以这样做。

非捕获lambda表达式是不使用周围上下文的(非恒定)变量并且不使用this(既不隐含也不显式)的lambda表达式。因此它不处于任何状态,因此消耗少量的内存。也没有可能对其他对象造成泄漏,因为引用其他对象是造成非捕获和捕获lambda表达式之间差异的原因,并且很可能是导致无法记住这种方式捕获lambda表达式的主要原因。

因此,此类永不收集的实例的最大数量等于应用程序中lambda表达式的总数,该总数可能为几百甚至数千,但与应用程序将要创建的对象总数相比仍然很小。如Function.identity() or t->t中所述,将lambda表达式放入工厂方法中而不是在源代码中重复它可以减少实例数量。但是,鉴于对象总数很少,这很少引起人们的关注。与已经提到的字符串文字或运行时中已经存在的Class对象的数量进行比较...

10-07 20:10