1. GC roots 枚举过程

1.1. 枚举过程的困难点:

范围大: GC roots主要在全局性的引用(常量与静态变量)和执行上下文(如栈帧中的本地变量表)。现在程序仅仅就方法区而言就很大,程序整体体积更是不用说。如果要注意检查这些引用,如查看所有栈帧中的本地变量表,那么时间上肯定是无法接受的。 必须在快照中进行:在枚举过程中,不能出现程序运行。因为这会导致GC roots不断变化,造成统计不准确的情况。 时间要求高:由于分析必须停止程序的运行,因此时间必须要短

1.2. OopMap

HotSpot是利用OopMap(Ordinary Object Pointer Map)来实现准确式GC的。当类加载完成后,HotSpot 就将对象内存布局之中什么偏移量上数值是一个什么样的类型的数据这些信息存放到 OopMap 中;在 HotSpot 的 JIT 编译过程中,同样会插入相关指令来标明哪些位置存放的是对象引用等,这样在 GC 发生时,HotSpot 就可以直接扫描 OopMap 来获取对象引用的存储位置,从而进行 GC Roots 枚举。

1.3. 安全点

程序并不能在任意地方都可以停下来进行GC,只有到达安全点时才能暂停。此外,在安全点中,HotSpot也会开始记录虚拟机的相关信息,如OopMap信息的录入。安全点的选择不能太少,否则GC等待时间太长;也不能太多,否则会增大运行负荷。其选择的原则为“是否具有让程序长时间执行的特征”,如方法调用,循环等等。具体安全点有下面几个:
循环的末尾 (防止大循环的时候一直不进入safepoint,而其他线程在等待它进入safepoint) 方法返回前 调用方法的call之后 抛出异常的位置 安全点暂停线程运行的手段有两种:抢先式中断和主动式中断。

1.4. 抢先式中断

不需要线程的执行代码主动配合,在GC发生时,首先把所有线程全部中断,如果发现有线程中断的地方不在安全点上,就恢复线程,让它跑到安全点上再暂停。不过现在的虚拟机几乎没有采用此算法的

1.5. 主动式中断

GC需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志,各个线程执行时去主动轮询查询此标志,发现中断标志为真时就中断自己挂起。轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方。

1.6. 安全区域

产生原因 安全点机制保证了程序执行时进入GC的问题。但是对于非执行态下,如线程Sleep或者Block下,由于此时程序(线程)无法响应JVM的中断请求,JVM也不太可能一直等待线程重新获取时间片,此时就需要安全区域了。
运行机理 在线程执行到Safe Region中的代码时,首先标识自己已经进入了Safe Region。当在这段时间里JVM要发起GC时,就不用管标识自己为Safe Region状态的线程了。当线程要离开Safe Region时,如果整个GC完成,那线程可继续执行,否则它必须等待直到收到可以安全离开Safe Region的信号为止

2. 准确式GC和保守式GC

准确式GC

HotSpot采用了准确式GC以提升GC roots的枚举速度。所谓准确式GC,就是让JVM知道内存中某位置数据的类型什么。比如当前内存位置中的数据究竟是一个整型变量还是一个引用类型。这样JVM可以很快确定所有引用类型的位置,从而更有针对性的进行GC roots枚举。
例如一个32位的区域可以是一个int整形数字也可以是一个对象引用。当一个对象进行执行时,需要修改指向这个内存的引用的指,非准确式GC也就是保守式GC不能完成这个任务。

  • 正确的根,可以直接识别出是指针还是非指针,都需要语言处理程序加工,即为准确式GC(Exact GC)
  • 打标签,将不明确的根的所有非指针与指针区分开。32位系统的指针是4的倍数,低2位一定是0,因此可以让非指针左移一位,而后将最后一位置1,如果溢出则换一个大的数据类型。
  • 不把寄存器和栈等当作根,而由处理程序来创建根。
  • 优点在于不存在指针不明确,可以使用复制移动算法。
  • 缺点则是需要语言处理程序对GC做支持,而且打标签等方式需要消耗资源与性能。

    保守式GC

    保守式GC指“不能识别指针和非指针的GC”

  • 不明确的根,寄存器、调用栈。全局变量空间等属于GC root,这些GC均不能识别出是指针还是非指针。

  • 指针的识别,在不明确的根的条件下可以已某种程度的精度来识别指针。
    • 是否被正确对其
    • 是否指向堆
    • 是否指向对象的头
  • 必须同时这三个基本的检查项目才可能是指针

不明确的根很可能出现数值但是同时满足以上三个条件产生”貌似指针的非指针(false point)”,此时GC采取一种保守的态度,把所有可疑的引用均当作指针,因此称作保守式GC。
优点: 有点在于容易编写语言处理程序。
缺点: 识别指针的成本、错误识别指针导致垃圾变成存活对象和不能使用复制算法等移动对象的GC算法。
https://liuzhengyang.github.io/2017/04/01/understandinggarbagecollect/