枚举根节点
当执行系统停顿下来后, 并不需要一个不漏的检查完所有执行上下文和全局的引用位置, 虚拟机应该是有办法直
接得知哪些地方存放着对象引用。在HotSpot的实现中, 是使用一组称为OopMap的数据结构来达到这个目的的
安全点
在Oop的协助下, HotSpot可以快速且准确地完成GC Roots枚举, 但会有一个问题, 可能导致引用关系变化, 或
者说OopMap内容变化地指令非常多, 如果为每一条指令都生成对应地OopMap, 那将会需要大量的额外空间, 这样
GC的空间成本将会变得更高
实际上, HotSpot并没有为每条指令都生成OopMap, 而只是在"特定的位置"记录这些信息, 这些位置称为安全点
(SafePoint), 即程序执行时并非在所有地方都能停顿下来开始GC, 只有在达到安全点时才能暂停
SafePoint的选定不能太少以至于让GC等待时间太长, 也不能过于频繁以至于过分增大运行时的负载。所以, 安
全点的选定基本是以"是否具有让程序长时间执行的特征"为标准进行选定的, 因为每条指令执行的时间非常短暂,
程序不太可能因为指令流长度太长这个原因而过长时间运行, "长时间执行"的最明显特征就是指令序列复用, 例
如方法调用、循环跳转、异常跳转等, 所以具有这些功能的指令才会产生SafePoint
对于SafePoint, 另一个需要考虑的问题是如何在GC发生时让所有线程(不包括执行JNI调用的线程)都迅速跑到
最近的安全点上再停顿下来: 抢占式中断和主动式中断
抢占式中断(几乎没有虚拟机采用该方式):
它不需要线程的执行代码主动去配合, 在GC发生时, 首先把所有线程全部中断, 如果有线程中断的地方不在安
全点上, 就恢复线程, 让它跑到安全点上
主动式中断:
当GC需要中断线程的时候, 不直接对线程操作, 仅仅简单地设置一个标志, 各个线程执行时主动去轮询这个标
志, 发现中断标志为真时就自己中断挂起。轮询标志的地方和安全点是重合的, 另外再加上创建对象需要分配
内存的地方
安全区域
使用安全点存在一个问题: 虽然该机制保证了程序执行时, 再遇到GC时能尽快地进入安全点并执行GC, 但是如果
程序"不执行"的时候, 即该线程没有分配到CPU时间, 如处于Sleep和Block状态, 这
时候线程就无法响应JVM的中断请求, JVM也显然不太可能等待线程重新分配CPU时间,
对于这种情况就需要用安全区域来解决
在线程执行到Safe Region安全区域中的代码时, 首先标识自己已经进入了安全区域, 那样, 当在这段时间里JVM
要发起GC时, 就不用管标识为安全区域状态的线程了, 在线程要离开安全区域的时候, 它要检查系统是否已经完
成了根节点枚举(或者是整个GC过程), 如果完成了, 那线程就继续执行, 否则它就等待知道收到可以安全离开安
全区域的信号为止