当在热点虚拟机上实现特定的垃圾收集算法时,必须严格考虑该算法的执行效率,以确保虚拟机的有效操作。通用垃圾收集算法可以读取此文章:常见的JVM Garbage Collection算法算法在Java中,有三种分析垃圾的主要方法:枚举根节点,安全点和安全区域。
在正式GC之前,有必要进行积极的分析,以标记可能在未来宣布死亡的对象。
从角度分析中,从GC根节点中找到参考链作为示例。收集垃圾时,收集线程将扫描堆栈上的内存以查看哪个位置存储参考类型。如果发现参考类型确实存储在某个位置中,则意味着它被引用的对象不能为这次回收了。但是问题是堆栈上的本地变量表中只有一部分数据。很大。
KAIDA分析还必须在快照中进行,该快照可以确保一致性 - 此处的“一致性”的含义意味着整个系统执行系统将在某个时间点冻结,并且不能出现。对象的参考关系仍在改变。如果不满足该点,则无法保证分析结果的准确性。这是导致GC暂停所有Java执行线程的重要原因之一,即“阻止世界”。
因此,每次都穿越整个堆栈空间绝对是不现实的。为了解决这个令人尴尬的问题,最早的保守GC和后来的准确的GCS.在现场,主流Java虚拟机是准确的GC。当执行系统暂停时,无需非裂口检查即可执行上下文和全局参考位置。essencein the Hotspot的实现,使用一组OOPMAP数据结构来实现此目的(OOP(Arting Object Pointer)指普通对象指针)。
加载类后,热点计算对象偏移的数据类型。在JIT汇编过程中,它还将记录引用堆栈和寄存器中的哪个位置。通过这种方式,GC在扫描时可以直接知道此信息。
执行GC时,会从某些已知位置(GC根)扫描记忆,并判断他是否可以指向GC堆中的指针(在这里它将涉及上和下边界检查(GC桩堆积在上限和下界,是已知的)和对齐检查(通常在分配空间时会有对齐要求。如果是4个字节对齐,那么不能以4的方式删除的数字绝对不是指针)。)。然后继续递归扫描,最后完成该模糊的判断方法被称为保守的GC,因为它无法准确确定位置是否真的指向GC堆指针。这种分析方法是因为它不需要准确地判断指针,因此效率很快,因此但是由于这个特征,他有以下两个明显的缺点:
与保守的GC相比,GC是准确的GC,什么是准确的GC?也就是说,我们准确地知道某个位置的指针是否适用于Java,也就是说,知道某个位置的数据类型,例如是否在当前的内存位置是整数变量或以这种方式参考typeessencein,您可以判断所有位置的数据是否是参考对GC堆的参考,包括堆栈和注册中的数据,以便枚举GC Roots更有针对性。
在Java中实现的方法是:从我的外部记录中记录的类型信息,以映射表存储,并将此映射表称为oopmap(oop(普通对象指针)),涉及一般对象指针,不同的对象指针),不同的)虚拟机的名称可能不同。Jrockit称为LiveMap,J9称为GC Map.Apache Harmony的DRLVM也称为GCMAP。
总而言之,当GC暂停时,虚拟机可以通过诸如OOPMAP之类的映射表知道对象偏移量中的哪种类型的数据,并且引用了堆栈和寄存器的位置。
关于对象访问定位,您可以阅读本文:详细说明Java对象的内存布局和访问定位。
OOPMAP记录局部变量与堆栈上对象之间的参考关系。这是一种更改空间时间的方式。在某些点,记录堆栈中引用的所有位置,因此您可以在真实GC时直接阅读它是真实的,而不是扫描一点。实际上,大多数主流虚拟机都这样做,例如热点。
在热点中,对象的类型信息记录其自己的OOPMAP,该信息记录了这种类型的对象中的哪种数据类型。从对象扫描可以是准确的;这些数据是在类加载过程中计算的。
为了实现此功能,还需要对虚拟机和JIT编译器的解释来生成OOPMAP。通常有两种生成此类映射表的方法:
在OOPMAP的协助下,热点可以快速准确地完成GC根枚举,但是一个非常现实的问题如下:可能会导致参考关系的变化,或者有许多指令将OOPMAP内容变化。将生成相应的OOPMAP。而且会有很多额外的空间,因此GC的空间成本将变得很高。
实际上,热点不会为每个指令生成OOPMAP。如前所述,每种JIT汇编之后的每种方法仅在某些特定位置下记录OOPMAP,并记录执行该方法的指令。在时间上,在堆栈和寄存器上的位置,这些位置被称为安全点。
执行程序后,不可能在所有地方停止开始GC,并且只有在达到安全点时才能暂停。如果安全点太小,GC等待时间太长。如果您选择太多,GC过于频繁。选择原理是“程序的长期执行的特征”,即目前可以重复使用的现有指令。
安全点的位置主要是:
当前的问题是在安全点中断线程的机理。有两种类型的方案:先例中断和主动中断。
SafePoint确保执行程序后,它将遇到一个可以在很长一段时间内输入GC的安全点。点安全性要挂起。在这次时,需要安全区域来解决此问题。
安全区域意味着在代码的一部分中,参考关系不会更改。GC可以安全地在该区域的任何位置启动GC。我们还可以将安全区域视为扩展的SafePoint。
当线程在安全区域中执行到代码时,第一个标识您输入了安全区域。在那个时候,当JVM即将在此期间启动GC时,就不必识别安全区域状态的线程。线程要离开安全区域时,必须检查系统是否已完成根系节点枚举(或整个GC过程)。如果完成了,则该线程将继续执行,否则必须等到可以安全地将其安全保留以安全地离开安全。