Java lambdas堆转储 - lambda实例未被垃圾回收
Java lambdas堆转储 - lambda实例未被垃圾回收
在使用Java生成应用程序时,我遇到了一些垃圾回收的问题。我在列表中使用Stream.map来修剪所有元素。尽管封闭类的实例为0,但匿名lambda类的实例仍然存在于堆转储中,如Visual VM的截图所示。\nLambdaTesting类:\n
class LambdaTesting { protected Listvalues; protected LambdaTesting(List values) { this.values = values; } public List modify() { return this.values.stream().map(x -> x.trim()).collect(Collectors.toList()); } public List modifyLocal() { List localValue = new ArrayList<>(); localValue.add("Local FOO "); localValue.add("Local BAR "); return localValue.stream().map(x -> x.trim()).collect(Collectors.toList()); } }
\n创建LambdaTesting实例并调用这些方法的方法:\n
public ListtestMethods() { List test = new ArrayList<>(); test.add("Global FOO "); test.add(" GLOBAL BAR"); LambdaTesting lambdaTesting = new LambdaTesting(test); lambdaTesting.modifyLocal(); lambdaTesting.modify(); }
\n在调用testMethods之后的下一行设置了一个断点,然后进行了线程转储。\n为什么堆转储中仍然存在对Lambda的引用?
Java lambdas heap dump - Instance of lambda not getting garbage collected
在这篇文章中,我们将讨论Java中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
对象的数量进行比较...
谢谢详细的解释。所以我相信,我不必担心堆转储中lambda表达式的实例。