1、通过OopMap完成根节点枚举

HotSpot虚拟机使用可达性分析算法确定对象是否可以被GC。

可达性分析算法从一系列GCRoot对象开始,向下搜索引用链,如果一个对象没有与任何GCRoot对象关联,这个对象就会被判定为可回收对象。

GCRoot包括以下对象:

  1. 虚拟机栈上的本地变量表引用的对象

  2. 方法区中类的静态属性引用的对象

  3. 方法区中常量引用的对象

  4. 本地方法栈中JNI引用的对象

这一过程称为根节点枚举,也就是垃圾回收中的标记过程,当前所有的垃圾收集器,在标记阶段都必须停止所有java执行线程(STW),以保证对象引用状态不会发生变化。

HotSpot虚拟机作为准确式虚拟机,维护了一个专门的映射表(OopMap)记录哪些位置存放着对象引用,来快速完成根节点枚举过程。

类加载完成,HotSpot就会把对象内某个偏移位置是否为对象引用记录下来,JIT编译过程中,也会在特定的位置记录下栈和局部变量表中哪些位置是引用。

2、安全点SafePoint

为每一个操作记录OopMap不现实,HotSpot虚拟机引入了SafePoint。安全点就是某些记录线程此时调用栈、寄存器等一些重要的数据区域里什么地方包含了GC要管理的指针(对象引用),而这些对象引用是通过OopMap结构进行记录的,可以直接通过对OopMap结构的访问来获得对象的引用。

SafePoint是程序中的某些位置,线程执行到这些位置时,线程中的某些状态是确定的,在safePoint可以记录OopMap信息,线程在safePoint停顿,虚拟机进行GC。一个线程可以在SafePoint上,也可以不在SafePoint上。一个线程在SafePoint时,它的状态可以安全地被其他JVM线程所操作和观测。

线程停顿方式有两种,抢先式中断和主动式中断:

  1. 抢先式中断:虚拟机需要GC时,中断所有线程,让没有到达SafePoint的线程继续执行至SafePoint并中断

  2. 主动式中断:虚拟机不直接中断线程,而是在内存中设置标志位,线程检查到标志位被设置,运行至SafePoint时主动中断

hotspot采用的是第一种, 也就是主动检测的方式. 而在主动检测的方式中又分为两种方式:

  1. 指定点执行检测代码
  2. polling page访问异常触发

Hotspot, 顾名思义, 就是热点的意思, 这里所谓的热点指的是热点代码, 也就是执行频率很高的代码, hotspot会根据运行时的信息来统计, 并将高频率执行的java字节码直接翻译成本地代码, 由此提高执行效率. 因此, hotspot有两种执行方式, 一个是解释执行, 一个是编译执行。指定点检测主要是解释执行用的,对于需要高效实现的地方,则采用polling page。

polling page和普通物理页面没什么区别,需要safepoint时, 会修改该页面的权限为不可访问, 这样编译的代码在访问这个页面时, 会触发段违规异常(SEGEV). 而hotspot在启动时捕获了这个异常, 当意识到是访问polling page导致时, 则主动挂起。

SafePoint一般出现在以下位置:

  1. 循环体的结尾

  2. 方法返回前

  3. 调用方法的call之后

  4. 抛出异常的位置

这些位置保证线程不会长时间运行而无法到达SafePoint,避免其他线程都停顿等待本线程。

public static void main(String[] args) throws Exception {
     [1]DemoObject demoObject = new DemoObject();
     [2]//往demoObject上挂一个字符串对象
     [3]demoObject.val1 = "this is a string object";
     [4]Thread.sleep(1000000);
}

我们知道代码是在线程里执行的, GC的代码也是在线程里执行, 如果执行GC的时候其他线程也同时执行的话, heap的状态将是难以追踪的. 以上面的代码为例, 假设GC线程通过扫描线程的stack(线程stack是一种GC Root), 扫描到demoObject, 然后根据这时候, main函数执行到[3], 但还未执行, 扫描的结果只发现demoObject是存活的, 接下来, main函数的线程执行[3], demoObject.val1引用了一个字符串对象, 这个对象的扫描就漏掉了, 除非以某种方式记录下这个变化, 然后重新扫描demoObject. 即便有办法记录这个赋值导致的变化然后再次扫描, 如果其他线程这时候又来捣乱, 那么重新扫描的时候有可能又发生了变化, 陷入循环…

再往下一点, 我们知道CPU执行运算时的数据, 需要从内存里载入寄存器中, 运算完再从寄存器存入内存, 对象的地址也要经过这么个过程. 假如一个java线程分配了一个对象A, 该对象的地址存在某个寄存器中, 然后线程的cpu时间片到期被切换出去, 同时GC的线程开始扫描存活对象, 由于没有路径到这个地址还在寄存器中的对象, 这个对象被认为是garbage, 回收了. 然后睡眠的java线程醒来了, 把寄存器中的对象地址赋值给了存活对象的某个字段, over…

GC的目的在于帮助我们收集不再使用的内存, 但是把正在是使用的内存当成垃圾回收显然是不能接受的. 同时通过分析也看到, 由于多线程运行环境的存在, GC的工作会变的异常复杂, 要安全的回收垃圾, 需要具备两个条件:

  • heap的变化是受限的, 当然了, 所有线程都停下来最好, 这样heap 在GC过程中是稳定的,这是最简单的情况.

  • heap的状态是已知的, 不会有活着的对象找不到或者很难找的情况. 想想对象地址在寄存器中的情况, 虽然可以有办法可以扫描线程的寄存器, 即使这样, 也必须知道哪个寄存器在某个时刻存的是地址, 要做到扫描不漏是很复杂的事情.

3、安全区SafeRegion

SafePoint无法解决线程未达到SafePoint并处于休眠或等待状态的情况,此时引入SafeRegion的概念。

SafeRegion是代码中的一块区域或线程的状态,在SafeRegion中,线程执行与否不会影响对象引用的状态。线程进入SafeRegion会给自己加标记,告诉虚拟机可以进行GC;线程准备离开SafeRegion前会询问虚拟机GC是否完成。

参考文章:

(1)聊聊JVM(六)理解JVM的safepoint https://blog.csdn.net/ITer_ZC/article/details/41847887

(2)聊聊JVM(九)理解进入safepoint时如何让Java线程全部阻塞 https://blog.csdn.net/ITer_ZC/article/details/41892567

hotspot的安全区(saferegion)和安全点(safepoint)的更多相关文章

  1. 虚拟机研究系列-「GC本质底层机制」SafePoint的深入分析和底层原理探究指南

    SafePoint前提介绍 在高度优化的现代JVM里,Safepoint有几种不同的用法.GC safepoint是最常见.大家听说得最多的,但还有deoptimization safepoint也很 ...

  2. JVM(四)垃圾回收的实现算法和执行细节

    全文共 1890 个字,读完大约需要 6 分钟. 上一篇我们讲了垃圾标记的一些实现细节和经典算法,而本文将系统的讲解一下垃圾回收的经典算法,和Hotspot虚拟机执行垃圾回收的一些实现细节,比如安全点 ...

  3. JVM自动内存管理:对象判定和回收算法

    可回收对象的判断方法 1.引用计数算法 2.可达性分析算法 引用计数算法 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就是 ...

  4. 没有二十年功力,写不出Thread.sleep(0)这一行“看似无用”的代码!

    你好呀,我是喜提七天居家隔离的歪歪. 这篇文章要从一个奇怪的注释说起,就是下面这张图: 我们可以不用管具体的代码逻辑,只是单单看这个 for 循环. 在循环里面,专门有个变量 j,来记录当前循环次数. ...

  5. 接口偶尔超时,竟又是JVM停顿的锅!

    原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介 继上次我们JVM停顿十几秒的问题解决后,我们系统终于稳定了,再也不会无故重启了! 这是之前的文章:耗时几个月,终于 ...

  6. HotSpot的算法实现

    1.枚举根节点 可达性分析中从GC Roots节点找引用,可作为GC Roots的节点主要是全局性的引用与执行上下文中,如果要逐个检查引用,必然消耗时间.另外可达性分析对执行时间的敏感还体现在GC停顿 ...

  7. Hotspot GC实现原理

    GC扫描 可达性分析的GC Roots主要是全局性引用或在Stack Frame中 ,现在的应用仅仅方法区往往就有几百兆,这样要这个检查这里面的引用,就必然会消耗很多时间,效率很低. 分析工作在一个保 ...

  8. 5.HotSpot的算法实现

    1.枚举根节点 在可达性分析中,可以作为GC Roots的节点有很多,但是现在很多应用仅仅方法区就有上百MB,如果逐个检查的话,效率就会变得不可接受. 而且,可达性分析必须在一个一致性的快照中进行-即 ...

  9. [Inside HotSpot] C1编译器工作流程及中间表示

    1. C1编译器线程 C1编译器(aka Client Compiler)的代码位于hotspot\share\c1.C1编译线程(C1 CompilerThread)会阻塞在任务队列,当发现队列有编 ...

随机推荐

  1. Django-xadmin的使用介绍

    Django-xadmin的介绍 Django是python的重量级web框架,写得少,做得多,非常适合后端开发,它很大的一个亮点是,自带后台管理模块,但它自带的后台管理有点丑,而Xadmin是基于b ...

  2. Django---Django的ORM的一对多操作(外键操作),ORM的多对多操作(关系管理对象),ORM的分组聚合,ORM的F字段查询和Q字段条件查询,Django的事务操作,额外(Django的终端打印SQL语句,脚本调试)

    Django---Django的ORM的一对多操作(外键操作),ORM的多对多操作(关系管理对象),ORM的分组聚合,ORM的F字段查询和Q字段条件查询,Django的事务操作,额外(Django的终 ...

  3. Java打印日历表

    今天来吐槽一下Java的Calendar类的使用问题,反正我是弄了半天. 首先是,遇到一个问题,输入年份和月份,需要打印这个月的日历,网上有不少代码,但我用了几个感觉都不是很靠谱. 然后经过一番探索, ...

  4. 使用JDBC,完成数据库批量添加数据操作:

    第一步:定义一个key String key = "into 表名(字段1,字段2,字段3)"; 第二步:定义一个可以增长的变量 StringBuffer values = new ...

  5. 43、css实现镂空半圆环

    <style> .circle { position: relative; box-sizing: border-box; } .big { width: 140px; height: 1 ...

  6. Java 流程控制 之 分支结构——条件判断语句

    一.判断语句 1.判断语句1-- 单 if 语句(单分支结构) 语法格式: if(条件表达式){ 语句体; }  执行流程: 首先判断条件表达式看其结果是 true 还是 false: 如果是 tru ...

  7. Win10 系统删除文件时提示文件不存在

    Win10系统使用一段时间后用户都会定期进行删除清理系统垃圾,减少系统盘的容量占用,但在删除的过程中许多用户都遇到无法删除的情况,这一次系统提示"文件不存在",这该怎么解决?我们可 ...

  8. RHEL6+GFS2+MYSQL高可用

    RHCS集群安装部署 组件介绍: luci: luci是一个基于web的,用来管理和配置RHCS集群,通过luci可以轻松的搭建一个功能强大的集群系统,节点主机可以使用ricci来和luci 管理段进 ...

  9. 软工团队第三次作业——编码组Alpha版本

    众志陈成组 柚荐--Alpha版本 编码部分 一.编码思路 思维导图如下 二.下载及操作方法 1.下载地址 GitHub地址:https://github.com/NyimaC/YouSuggest ...

  10. PAT 乙级 1040.有几个PAT C++/Java

    题目来源 字符串 APPAPT 中包含了两个单词 PAT,其中第一个 PAT 是第 2 位(P),第 4 位(A),第 6 位(T):第二个 PAT 是第 3 位(P),第 4 位(A),第 6 位( ...