浅谈JVM的垃圾收集——OopMap与安全点

背景

前面的文章介绍了可作为GC ROOT的节点包括:

  • 虚拟机栈中引用的对象
  • 方法区中常量和静态属性引用的对象
  • 所有被同步锁(synchronized关键字)持有的对象
  • Java虚拟机内部的引用(如系统类加载器,常驻的异常对象等等)
  • 本地方法栈中引用的对象等等

具体可看这篇文章——浅谈JVM的垃圾收集(一)

固定可作为GC Roots的节点主要在全局性的引用(例如常量或类静态属性)与执行上下文(例如 栈帧中的本地变量表中)。如果要逐个检查以这里为起源的引用肯定得消耗不少时间,怎么才能做到高效的查找引用呢?

OopMap(Ordinary Object Pointer,普通对象指针)

迄今为止,所有收集器在根节点枚举(即查找GC ROOT)这一步骤都是必须暂停用户线程的。当用户线程暂停后,在查找引用时并不需要一个不漏的检查完所有执行上下文和全局的引用位置。实际上虚拟机是通过一组叫OopMap的数据结构得知哪些位置存放了引用类型。在类加载完成的时候,虚拟机会将对象内什么偏移量什么数据计算出来,在JIT编译的时候,会在特定的位置记住栈和寄存器中什么地方存在引用,GC直接对这些引用进行扫描。

总结oopMap的作用

  • 避免从全局性引用和执行上下文中逐个查找GC ROOT,加快枚举根节点的速度
  • 可以帮助HotSpot实现准确式GC

安全点

问题:为什么OopMap要在特定的位置才记录栈中对象的引用呢?

因为在方法执行过程中,可能会有很多命令导致引用关系变化,如果每次引用关系变化都要去更新OopMap,那成本是很高的。所以只有代码执行到特定的位置的时候,OopMap才记录栈中对象的引用的位置。

这些特定的位置就是安全点。安全点的设定,决定了用户线程并非在代码流的任意位置都能停下来做垃圾收集,而是强制要求必须执行到达安全点后才能够暂停。即只有在达到这样的安全点才能暂停下来进行GC。

安全点的选取的太少会导致收集器等待时间过长,太多会导致收集动作太频繁以至于增大运行时的内存负荷。

安全点位置的选取

基本上是以“是否具有让程序长时间执行的特征”为标准进行选定的,“长时间执行”的最明显特征就是指令序列的复用,例如方法调用、循环跳转、异常跳转等都属于指令序列复用,所以只有具有这些功能的指令才会产生安全点

如何让用户线程在要进行GC时都跑到最近的安全点停顿下来呢?有下面两种方案选择:

  • 抢占式中断
  • 主动式中断

现在的虚拟机都是采用主动式中断。主动式中断的思想:设置一个中断标志,线程执行过程中不断的轮询这个标志,一旦发现中断标志为真时就自己在最近的安全点上主动中断挂起。

由于轮询操作是很频繁的事,所以HotSpot把轮询操作精简至只有一条test汇编指令的程度

在这里插入图片描述

安全区域(Safe Region)

上面说了安全点的设置会让线程在垃圾收集前,跑到最近的安全点主动挂起自己。但是如果线程处于Sleep或者Block状态,线程无法响应虚拟机的中断请求,不能再走到安全的地方去中断挂起自己。对于这种情况,就必须引入安全区域来解决。

安全区域是指能够确保在某一段代码片段之中,引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾收集都是安全的。我们也可以把安全区域看作被扩展拉伸了的安全点。

简单的说就是对处于Sleep或者Block状态的线程,它的方法无法执行,所以方法内对象的引用关系肯定不会改变,即OopMap不用做任何更新

总结

OopMap的作用

  • 避免逐个查找栈和方法区中的GC ROOT,加快枚举根节点的速度
  • 可以帮助HotSpot实现准确式GC

安全点的作用

  • 避免频繁更新OopMap,只在达到安全点的位置才更新OopMap
  • 用户线程在安全点停顿,GC在安全点进行
  • 线程轮询访问中断标志位,主动跑去安全点挂起

安全区域的作用

对处于Sleep或者Block状态的线程,由于代码中的引用关系不会发生变化,即OopMap无需更新,所以在此区域进行GC是安全的。此区域就叫安全区域。

OopMap和GC ROOT的关系
OopMap用于直接记录在方法区和栈中的GC ROOT,避免逐个查找方法区和栈。
记忆集与OopMap的联系与区别
共同点:他们都是用于获取GC ROOT
不同点:OopMap记录的是准确的GC ROOT。而记忆集记录的是 包含跨代引用的GC ROOT 的一块内存,还要再扫描这块内存以得到GC ROOT。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值