假设我有以下代码:
public void process() {
byte[] data = new byte[size];
... // code that uses the above data
longProcess(); // a very long running process that does not use the data.
}
假设数据未在程序中的任何其他位置引用,那么JVM是否足够智能以允许在长进程仍在运行时对数据进行垃圾回收?
如果没有,将添加
data = null;
在漫长的过程之前允许这种情况发生?
答案 0 :(得分:6)
这取决于JVM。我尝试过的Oracle JVM版本(1.6.0_41和1.7.0_09)默认情况下不执行此优化。但是,1.7.0_09会在启用积极优化时执行此操作。
这是我进行过的测试:
public class Main {
public static int g() {
int n = 100000;
int arr[][] = new int[n][];
for (int i = 0; i < n; ++i) {
try {
arr[i] = new int[100000];
} catch (OutOfMemoryError ex) {
return i;
}
}
return -1;
}
public static void f1() {
int arr[] = new int[1000000];
System.out.println(g());
}
public static void f2() {
int arr[] = new int[1000000];
arr = null;
System.out.println(g());
}
public static void main(String[] argv) {
for (int j = 0; j < 2; ++j) {
for (int i = 0; i < 10; ++i) {
f1();
}
System.out.println("-----");
for (int i = 0; i < 10; ++i) {
f2();
}
System.out.println("-----");
}
}
}
将JVM 1.7与默认设置一起使用,f1()
在3195次迭代后始终耗尽内存,而f2()
始终管理3205次迭代。
如果使用带有-XX:+AggressiveOpts -XX:CompileThreshold=1
的Java 1.7.0_09运行代码,则图片会发生变化:两个版本都可以执行3205次迭代,这表明HotSpot在这种情况下确实执行了此优化。 Java 1.6.0_41似乎没有这样做。
在我的测试中,限制数组的范围与设置引用null
具有相同的效果,如果您觉得应该帮助JVM尽快收集数组,那么它应该是首选。
答案 1 :(得分:1)
考虑到编写的代码,在longprocess()执行期间,数组肯定不会被垃圾收集,因为堆栈上的数组仍然有一个范围的引用。一旦声明了该数组,在删除所有对它的引用之前,它就不符合垃圾收集的条件。你的行
data = null;
将删除对它的一个引用,但根据您的处理代码,它可能不是唯一的引用。如果所有引用都被删除了,那么垃圾收集器可以在longprocess()返回时很好地收集该数组的内存,尽管这不能保证。
答案 2 :(得分:0)
如果没有数据引用,那么GC将完成这项工作。
答案 3 :(得分:0)
在处理方法完成后,数据数组才会被内存释放。因此,如果您希望编译器释放内存,则必须在代码中显式添加data = null。
垃圾收集器只释放没有有效引用的内存,并且没有其他方法可以再次指向该内存。