好的,让我看看我是否可以解释。
我有一些代码在Scala Stream中包装了一个Java迭代器(来自Hadoop),因此可以通过我无法直接控制的客户端代码多次读取它。使用此Stream完成的最后一件事是reduce()操作。 Stream会记住它已经看过的所有项目。不幸的是,在某些情况下,迭代器将非常大,因此将所有项目存储在其中将导致内存不足错误。但是,一般来说,客户端代码需要多次迭代工具的情况与内存破坏迭代器的情况不同,如果确实存在这种情况,那不是我的问题。
我想要确保的是,我可以为需要它的代码提供memoizing功能,但不能为不需要它的代码提供(特别是对于从不查看Stream的代码)。
Stream中的reduce()代码表示它的编写方式允许GC已经访问过的部分的GC发生而减少。所以,如果我能确保实际发生这种情况,我会没事的。但在实践中我怎样才能确保这种情况发生?特别是,如果函数A创建并将流传递给函数B,函数B将流传递给函数C,函数C则调用reduce(),那么仍然在函数A,B和C中对流的引用呢? ?在所有这些情况下,尽管调用不一定是尾递归的,但在三个函数中的任何一个中都不会进一步使用流。 JVM是否足够智能,以确保在调用reduce()时函数A,B和C的引用计数为0,以便GC可以发生?本质上这意味着JVM在函数A中注意到它对项目执行的最后一件事是调用函数B,因此它在调用B的同时消除了自己的句柄,同样对于B到C,C也减少了( )。
如果这种方法正常,如果A,B或C有一个固定在该项目上的局部变量,它是否也有效? (之后再说不会使用它。)那是因为在没有使用本地变量的情况下对它进行正确编码会更加棘手。
答案 0 :(得分:2)
范围内但永远不会读取的变量 dead 。为了垃圾收集,JVM可以自由地忽略死变量;只有死变量指向的对象是不可达的,并且可以被收集。 JLS的相关位置,§12.6.1 Implementing Finalization,模糊不清,其中说:
可到达对象是任何可以从任何活动线程继续计算中访问的对象。
并解释说:
可以设计优化程序的转换,以减少可达到的对象数量,使其少于可以被认为可达的对象数量。例如,Java编译器或代码生成器可以选择设置一个不再用于null的变量或参数,以使这种对象的存储可以更快地回收。
如果对象字段中的值存储在寄存器中,则会出现另一个示例。然后程序可以访问寄存器而不是对象,并且永远不会再次访问对象。这意味着该对象是垃圾。请注意,仅当引用位于堆栈上且未存储在堆中时,才允许此类优化。
如果你的方法A只有引用流的死变量,那么它就不会妨碍它的收集。
但是,请注意,这意味着局部变量:如果您有引用流的字段(包括封闭嵌套类的方法中的封闭局部变量),则不适用;我不认为JVM被允许将这些视为死亡。换句话说,在这里:
public Callable<String> foo(final Object o) {
return new Callable<String>() {
public String call() throws InterruptedException {
String s = o.toString();
Thread.sleep(1000000);
return s;
}
};
}
在收集匿名o
之前,无法收集对象Callable
,即使它在toString
调用后从未使用过,因为在...中有一个合成字段引用它Callable
。