我最近阅读了有关垃圾收集的部分内容(主要是在Java中),还有一个问题仍然没有答案:JVM(或一般的运行时系统)如何跟踪当前的活动对象?
我理解对象是当前堆栈中的对象,因此所有局部变量或函数参数都是ARE对象。这个问题的问题是每当运行时系统检查当前堆栈上的内容时,它如何区分引用变量和简单的int?它不能,可以吗?
因此,必须有某种机制允许运行时构建活动对象的初始列表以通过标记扫描阶段......
答案 0 :(得分:6)
我发现greyfairer提供的答案是错误的。 JVM运行时不会通过查看用于在堆栈上推送数据的字节码来从堆栈收集根集。堆栈帧由4字节(32位拱)插槽组成。每个槽可以是对堆对象的引用,也可以是诸如int的原始值。当需要GC时,运行时会从上到下扫描堆栈。对于每个插槽,它包含以下参考:
一个。它以4字节边界对齐。
湾插槽中的值指向堆的区域(在下限和上限之间)。
℃。 allocbit已设置。 allocbit是一个标志,指示是否分配了与之对应的内存位置。
以下是我的参考:http://www.ibm.com/developerworks/ibm/library/i-garbage2/。
还有一些其他技术可以找到根集(不是在Java中)。例如,因为指针通常在4/8字节边界对齐,所以第一位可用于指示槽是原始值还是指针:对于原始值,第一位设置为1.缺点是您只有31位(32位拱)来表示整数,并且对原始值的每个操作都涉及移位,这显然是开销。
此外,您可以在堆上分配包括int的所有类型。也就是说,所有东西都是物体。然后,堆栈帧中的所有插槽都是引用。
答案 1 :(得分:2)
运行时可以完美地区分引用变量和基元,因为它位于编译的字节码中。
例如,如果函数f1调用函数f2(int i,Object o,long l),则调用函数f1将在堆栈(或寄存器)中推送4个字节,表示i,4(或8?)引用o的字节数,l的8个字节。被调用函数f2知道在堆栈上找到这些字节的位置,并且可能将引用复制到堆上的某个对象,或者不可以。当函数f2返回时,调用函数将从堆栈中删除参数。
运行时解释字节码并记录它在栈上推或放的内容,因此它知道什么是引用,什么是原始值。
根据http://www.javacoffeebreak.com/articles/thinkinginjava/abitaboutgarbagecollection.html,java使用tracing garbage collector而不是引用计数算法。
答案 2 :(得分:1)
HotSpot VM为每个编译的子例程生成GC映射,其中包含有关根的位置的信息。例如,假设它已经编译了一个子程序到机器代码(字节代码的原理是相同的),它是120字节长,那么它的GC映射可能看起来像这样:
[RSP+x]
这里R??
应该表示堆栈位置和RBX
寄存器。因此,如果线程在偏移量10处的汇编指令处停止并且gc循环运行,则HotSpot知道三个根位于RSI
,[RSP+0]
和{{1}}中。它跟踪那些根并在必须移动对象时更新指针。
我为GC地图描述的格式仅用于演示原理,显然不是HotSpot实际使用的格式。它不完整,因为它不包含有关包含原始实时值的寄存器和堆栈槽的信息,并且对每个指令偏移使用列表不是空间有效的。您可以通过多种方式以更有效的方式打包信息。