4.高效安全地发起垃圾收集

高效安全地发起垃圾收集

可达性分析安全性和效率问题

虽然可达性分析的算法本身很简明,但是在实践中还是有不少其他问题需要解决的。
第一个问题便是在多线程环境下,会出现分析过程中对象引用关系还在不断变化的情况。比如垃圾回收器可能回收事实上仍被引用的对象内存,一旦从原引用访问已经被回收了的对象,则很有可能会直接导致 Java 虚拟机崩溃。这样无法保证安全性。
另外一个问题是GCRoot节点主要在全局性引用(例如常量和类静态属性)与执行上下文(例如栈帧中的本地变量表),现在很多应用仅方法区就有数百兆,如果逐个检查里面的引用必然非常耗时,效率低下。

枚举根节点

怎么解决以上两个问题呢?
先放一张图,可以根据改图理解问题的解决方案。
在这里插入图片描述

  • 第一个问题,在 HotSpot 虚拟机里,采用的是一种简单粗暴的方式,那便是 Stop-the-world,停止其他非垃圾回收线程的工作,直到完成垃圾回收。这也就造成了垃圾回收所谓的暂停时间(GC pause)。
    Java 虚拟机中的 Stop-the-world 是通过安全点(safepoint)机制来实现的。当 Java 虚拟机收到 Stop-the-world 请求,它便会等待所有的线程都到达安全点,才允许请求 Stop-the-world 的线程进行独占的工作。当然,安全点的初始目的并不是让其他线程停下,而是找到一个稳定的执行状态。在这个执行状态下,Java 虚拟机的堆栈不会发生变化。这么一来,垃圾回收器便能够“安全”地执行可达性分析。
  • 对于第二个问题,在HotSpot中,使用一组OopMap的数据结构来标记对象引用的位置。在类加载完成的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来。在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。

安全点

在OopMap的协助下,我们可以快速的完成GC Roots枚举,但我们也不能随时随地都生成OopMap,那样一方面会需要更多的空间来存放这些对象,另一方面效率也会简单低下。所以只会在特定的位置来记录一下,主要是正在:

  • 循环的末尾
  • 方法临返回前/调用方法的call指令后
  • 可能抛异常的位置

对于Sefepoint,另一个需要考虑的问题是如何在GC发生时让所有线程(这里不包括执行JNI调用的线程)都“跑”到最近的安全点上再停顿下来。
这里有两种方案可供选择:抢先式中断(Preemptive Suspension)和主动式中断(Voluntary Suspension)。

  • 在抢先式中断中不需要线程主动配合,在GC发生的时候就让所有线程都中断,如果发现哪个线程中断的地方不在安全点上,那么就恢复线程,然后让它跑到安全点上。
  • 主动式中断是让GC在需要中断线程的时候不直接对线程操作,设置一个标志,让各个线程主动轮询这个标志,如果中断标志位真时就让自己中断。

安全区域

使用Safepoint似乎已经完美地解决了如何进入GC的问题,但实际情况却并不一定。Safepoint机制保证了程序执行时,在不太长的时间内就会遇到可进入GC的Safepoint。但是,程序“不执行”的时候呢?所谓的程序不执行就是没有分配CPU时间,典型的例子就是线程处于Sleep状态或者Blocked状态,这时候线程无法响应JVM的中断请求,“走”到安全的地方去中断挂起,JVM也显然不太可能等待线程重新被分配CPU时间。对于这种情况,就需要安全区域(Safe Region)来解决。

安全区域是指在一段代码片段之中,引用关系不会发生变化。在这个区域中的任意地方开始GC都是安全的。我们也可以把Safe Region看做是被扩展了的Safepoint。在线程执行到Safe Region中的代码时,首先标识自己已经进入了Safe Region,那样,当在这段时间里JVM要发起GC时,就不用管标识自己为Safe Region状态的线程了。在线程要离开Safe Region时,它要检查系统是否已经完成了根节点枚举(或者是整个GC过程),如果完成了,那线程就继续执行,否则它就必须等待直到收到可以安全离开Safe Region的信号为止。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值