资讯专栏INFORMATION COLUMN

safe-point(safepoint 安全点) 和 safe-region(安全区域)

baoxl / 3046人阅读

摘要:当暂停了它的执行时,只有将所有引用信息保存在其上下文中,才能枚举根的集合,这意味着,需要能够告知那些栈的槽位有一用,那些寄存器持有引用。而真正有机会成为暂停点的地方就称作,即能够安全的枚举根集合的暂停点。

以 GC safe-point引入
GC如何找到不可用的对象

编写代码的时候是可以知道对象不可用的,但对于程序来说,需要一定的方式来知晓,可用方法比如:编译分析,引用计数,和对象是否可达

可达性分析

一个对象只要能够通过mutator触达,那么它就是“活”着的。如果Mutator栈的一个槽位包含了对象的引用,那么对象就是直接可触达。而从直接可达对象可触达的对象必定也是可达的,因而可达性分析,只需要找到直接可达的引用。

直接可达的引用就是根引用,根引用的集合就是根的集合

mutator的上下文就包含了直接可达的数据,所以要获取对象根集合就是要找到mutator上下文中的对象引用,而mutator的上下文指的就是它的栈、它的寄存器文件以及一些线程上特定的数据

全局数据本身也是直接可达的

可达性分析为了确保能正确的决定对象是否存活,GC需要获取mutator 上下文的一致性快照,然后枚举所有的根对象。

这里的一致性指的是 快照的抽取就像只在一个时间点发生,来避免丢失一些活着的对象
如何获取 mutator上下文的一致性快照

一种简单的方式就是在跟引用的过程中暂停所有的线程。当mutator暂停了它的执行时,只有将所有引用信息保存在其上下文中,才能枚举根的集合,这意味着,mutator需要能够告知那些栈的槽位有一用,那些寄存器持有引用。如果GC能够准确的获取上述引用信息,它就称作精准根集合枚举。

无法获取就是不精准的,以下只讲精准的
如何获取精准的引用信息枚举

对于java来说,JIT知晓所有的栈帧信息和寄存器的内容,当JIT编译一个方法时,对于每条指令,它都可以去保存根引用信息,保存意味着额外的存储空间,如果要存储所有的指令就显得花销太大,另外在真实的运行过程中也只有少数指令才会成为暂停点,因此JIT只需要保存这些指令点的信息就够了。而真正有机会成为暂停点的地方就称作 safe-points,即能够安全的枚举根集合的暂停点

safe-point 定义

“A point in program where the state of execution is known by the VM”,即代码中VM能够准确知道执行状态的位置。
safe-point有多个种类

GC safepoint,要触发一次GC,JVM中的所有线程都必须达到GC safepoint

Deoptimization safepoint,要触发一次 deoptimization,需要执行deoptimization的线程要到达safepoint之后才可以开始deoptimize

Hotspot中两者实现在一起,概念上没有直接联系,需要数据不一样
如何保证mutator会在 safe-point暂停

当GC想要触发一次回收时,它会设置一个标志,mutator则周期性的去检查(poll)这个标志,如果检查到了,就会立马暂停,这里的检查点(poll points)也是安全点,由JIT负责把poll points放到合适的位置

那些地方适合设置检查GC事件的标记

polling point插入的主要原则是:

polling point应该足够多,防止GC等一个mutator的暂停太长,导致其他mutator都走在等GC释放空间,程序整个等待过长

polling point不能太频繁导致运行时存储开销过大

polling本身也是有开销的,不能过多

权衡下来只在必须和必要的地方加

在分配地址的时候强制添加,因为分配空间很有肯能导致回收,所以这里是一个安全点

长时间的执行一般意味着循环和方法调用,所以方法调用和循环返回最好加上

但是有时候并不是长时间的执行,而是长时间的空闲,比如 sleep、block,线程在执行其他的native函数,这些时候JVM无法掌控执行能力,也就无法响应GC事件。

不同的JVM选用不同的位置放置safepoint。
如何解决sleep/block 带来的问题

引用safe-region。safe-region是指代码快中没有用到会变异的部分,这样的代码块中,任何一个点都可以安全的枚举根。当进入到safe-region中时,mutator会设置一个准备标记,在离开safe-region区域之前,会检查GC是否已经完成了回收,如果没有,那么就暂停执行,如果有,就可以直接离开safe-region区域,不需要暂停mutator

文章翻译自 Xiao-Feng Li 博客

rednaxelafx对safepoint的回答

总结

代码的执行过程中,如果需要执行某些操作,比如GC,deoptimize,等等,必须知道当前程序所有线程运行到的地方,是否能够恰好满足我执行对应操作,而不会对应用程序本身造成损害,这些能够正确执行操作的地方也就是safepoint/saferegion

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72725.html

相关文章

  • Java并发编程之旅总览

    摘要:线程安全的概念什么时候线程不安全怎样做到线程安全怎么扩展线程安全的类对线程安全的支持对线程安全支持有哪些中的线程池的使用与中线程池的生命周期与线程中断中的锁中常见死锁与活锁的实例线程同步机制显示锁使用与原理原理剖析原理中的与原理偏向锁状态 showImg(https://segmentfault.com/img/bVblUE9?w=1354&h=1660); 线程安全的概念 showI...

    Harpsichord1207 评论0 收藏0
  • 谈一谈JVM垃圾回收

    摘要:这个算法看似不错而且简单,不过存在这一个致命伤当两个对象互相引用的时候,就永远不会被回收于是引用计数算法就永远回收不了这两个对象,下面介绍另一种算法。 前言 ​ 如果要问Java与其他编程语言最大的不同是什么,我第一个想到的一定就是Java所运行的JVM所自带的自动垃圾回收机制,以下是我学习JVM垃圾回收机制整理的笔记,希望能对读者有一些帮助。 哪些内存需要回收?what? ​ ...

    stormzhang 评论0 收藏0
  • 《Java Concurrency in Practice》中三个VehicleTracker例子的

    摘要:线程安全需求分析三个例子都是关于车辆追踪的。他们使用了不同的方式来保证车辆追踪类的线程安全性。值得注意的值文档也是维护线程安全的重要组成部分。 每个例子后面有代码,大家可以先把代码粘出来或者开两个页面,先过一下例子的代码,然后一边看分析一遍看代码,上下拖动看的话效果不好。 欢迎拍砖和补充。 线程安全需求分析 三个例子都是关于车辆追踪的。他们使用了不同的方式来保证车辆追踪类的线程安全性。...

    binaryTree 评论0 收藏0
  • Java线程那事儿

    摘要:然而对于一个和关联的线程来说在线程被创建和更新他的之前会有一个小窗口,因此必须检查这种情况为线程结构体分配内存并创建线程。可能是,因此我们耗尽了内存太多的活跃线程。代码执行到这,线程还是状态,因为线程必须被创建者直接启动。 引言 说到Thread大家都很熟悉,我们平常写并发代码的时候都会接触到,那么我们来看看下面这段代码是如何初始化以及执行的呢? public class Thread...

    silvertheo 评论0 收藏0
  • 关于偏向锁,安全,JIT的一些暗坑.

    摘要:前言本文是一篇简短的杂糅本文源自于作者最近的一个疑问为什么在旧版的中偏向锁的移除一定要在全局安全点进行同时在上个星期作者参与的一个项目发生了一件怪事一个服务莫名其妙地不接受任何请求了一切请求都是而查看日志发现出故障的服务本身去请求另一个服务 前言 本文是一篇简短的杂糅. 本文源自于作者最近的一个疑问:为什么在旧版的jdk中偏向锁的移除一定要在全局安全点进行?同时在上个星期,作者参与的一...

    JeOam 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<