我试图了解GC的行为,但发现了一些我无法理解的有趣的东西。
请查看代码和输出:
public class GCTest {
private static int i=0;
@Override
protected void finalize() throws Throwable {
i++; //counting garbage collected objects
}
public static void main(String[] args) {
GCTest holdLastObject; //If I assign null here then no of eligible objects are 9 otherwise 10.
for (int i = 0; i < 10; i++) {
holdLastObject=new GCTest();
}
System.gc(); //requesting GC
//sleeping for a while to run after GC.
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// final output
System.out.println("`Total no of object garbage collected=`"+i);
}
}
在上面的示例中,如果我将
holdLastObject
分配为null,则得到Total no of object garbage collected=9
。如果没有,我得到10
。有人可以解释吗?我找不到正确的原因。
最佳答案
我怀疑这是由于任务明确。
如果在循环之前为holdLastObject
分配了一个值,则肯定是为整个方法分配的(从声明开始)-因此,即使您在循环后没有访问它,GC也会知道您可以编写代码访问它,因此它不会最后确定实例。
由于您没有在循环之前为变量分配值,因此除了循环内之外,其他变量均未分配值-因此我怀疑GC会将其视为在循环中声明的值-它知道循环后没有代码可以读取从变量中获取(因为未明确分配),因此它知道可以完成并收集最后一个实例。
如果您添加以下内容,只是为了澄清我的意思:
System.out.println(holdLastObject);
就在
System.gc()
行之前,您会发现它在第一种情况下(没有分配)不会编译。我怀疑这是VM的详细信息-我希望,如果GC可以证明实际上没有从本地变量中读取任何代码,则无论如何收集最终实例都是合法的(即使不是) t目前已采用这种方式)。
编辑:与TheLostMind的答案相反,我相信编译器会将这些信息提供给JVM。使用
javap -verbose GCTest
我发现这没有分配: StackMapTable: number_of_entries = 4
frame_type = 253 /* append */
offset_delta = 2
locals = [ top, int ]
frame_type = 249 /* chop */
offset_delta = 19
frame_type = 75 /* same_locals_1_stack_item */
stack = [ class java/lang/InterruptedException ]
frame_type = 4 /* same */
并附有以下内容:
StackMapTable: number_of_entries = 4
frame_type = 253 /* append */
offset_delta = 4
locals = [ class GCTest, int ]
frame_type = 250 /* chop */
offset_delta = 19
frame_type = 75 /* same_locals_1_stack_item */
stack = [ class java/lang/InterruptedException ]
frame_type = 4 /* same */
注意第一个条目的
locals
部分的区别。奇怪的是,如果没有初始分配,class GCTest
条目不会出现在任何地方...