zoukankan      html  css  js  c++  java
  • jvm读书记录4-HotSpot的算法细节实现

    HotSpot的算法细节实现

    一、根节点枚举

    迄今为止,所有收集器在根节点枚举这一步骤时都是必须暂停用户线程的,现在可达性分析算法耗时最长的查找引用链的过程已经可以和用户线程一起并发,但是根节点枚举始终还是必须在一个能保障一致性快照中才得以进行。

    由于目前Java虚拟机使用的都是准确式垃圾收集,所有当用户线程停顿下来之后,其实并不需要一个不漏的检查完成所有执行上下文和全局的引用位置,虚拟机应当是有办法直接得到哪些地方存放着对象的引用的。在hotspot的解决方案里面,是使用一组称为oopmap的数据结构来达到这个目的。

    一旦类加载动作完成的时候,hotspot就会把对象内什么偏移量上是什么类型的数据计算出来,在即时变异过程中,也会在特定的位置记录下栈里和寄存器里哪些位置是引用,这样收集器在扫描时就可以直接得知这些信息了,并不需要真正一个不漏地从方法区等gc roots开始查找。

    二、安全点

    在oopmap的协助下, hotspot可以快速准确的完成gc roots枚举,但是导致oopmap内容变化的指令非常多,如果为每条指令都声称oopmap,那么将会需要大量额外的内存空间。

    因此只有在“特定的位置”记录了这些信息(例如方法调用、循环跳转、异常跳转),这些位置称为安全点,有了安全点的设定,也就决定了用户程序执行时并非在代码指令流的任意位置都能够停顿下来开始垃圾收集,而是强制要求必须执行到安全点后才能够暂停。

    对于安全点,另外一个需要考虑的问题时,如何在垃圾收集发生时让所有的线程都跑到最近的安全点,然后停顿下来,这里有俩种方案可供选择:抢先式中断和主动式中断,抢先式中断不需要线程的执行代码主动配合,在垃圾收集发生时,系统首先把所有用户线程中断,如果有用户线程中断的地点不是安全点,就恢复这个线程让他重新执行到安全点。现在几乎没有虚拟机采用抢先式中断来暂停线程响应gc事件。主动式中断时当垃圾收集需要中断线程的时候,只是简单的设置一个标志位,各个线程执行过程时会不停的主动去轮训这个标识,一旦发现中断标志为真就自己在最近的安全点主动中断关起。轮训标志和安全点时重合的,hotspt虚拟机使用内存保护陷阱的方式,把轮训操作精简至只有一条汇编指令的程度。通过test指令产生一个自陷异常信号,然后在预先注册的异常处理器中挂起线程实现等待。

    三、安全区域

    当用户的线程sleep或者blocked转态,这时候线程无法响应虚拟机的中断请求,因此引入安全区域来解决这类问题。

    安全区域指能够确保在一段代码片段中,引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾收集都是安全的。当用户线程执行到安全区域里面的代码时,首先会标记自己已经进入了安全区域,那样当这段时间里虚拟机要发起垃圾收集时就不必去管这些已声明自己在安全区域的线程了,当线程要离开安全区域时,他要检查虚拟机是否已经完成了根节点枚举。

    四、记忆集与卡表

    记忆集时一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。其时为了标记跨代引用,方便在gc roots扫描时把整个老年代加进gc roots扫描范围。

    收集器只需要通过记忆集判断出某一块非收集区域是否存在有指向了收集区域的指针可以了,并不需要了解这个跨代指针的全部细节,

    那么在实现记忆集的时候买就可以选择更为粗旷的记录精度来节省记忆集的存储和维护成本,包括:

    1、字长精度:记录精确到机器字长

    2、对象精度:每个记录精确到一个对象

    3、卡精度:每个记录精确到一块内存区域,其实现时用一种称为“卡表”的方式去实现记忆集,这也是目前最常用的一种记忆集实现形式。

    五、写屏障

    使用记忆集可以缩减gc roots扫描范围的问题,但是卡表元素如何维护呢?例如他们何时变脏、谁来把他们变脏。

    何时变脏:有其他分代区域的对象引用了本区域的对象时

    如何变脏:在hotspot虚拟机里时通过写屏障技术维护卡表状态的;

    写屏障可以看做是虚拟机层面对“引用类型字段赋值”这个动作的aop切面,在引用对象赋值时会产生一个环形通知,供程序执行额外的动作,也就是说赋值的前后都在写屏障的覆盖范围。

    即写前屏障和写后屏障。hotspot虚拟机的许多收集器都是用的写后屏障,排除g1收集器。

    卡表在高并发的场景下面临着伪共享的问题,现代中央处理器的缓存系统中时以缓存行为单位存储的,当多线程修改相互独立的变量时,如果这些变量恰好共享同一个缓存行,就会彼此影响,导致性能降低,解决这个额问题一个简单的方案是不采用无条件的写屏障,而是先检查卡表标记,只有卡表未被标记时,才将其标记为变脏。

    六、并发的可达性分析

    标记阶段时追踪式垃圾收集算法的共同特征,而且必须在一个能保障一致性快照上才能进行对象图的遍历。同时随着堆上堆上越多, 其扫描的时间也就越长。

     当用户的线程和收集器并发工作的时候,可能导致“对象消失” (三色标记分析 此处略)

    要解决对象消失的问题有俩种解决方案:增量更新、原始快照

    增量更新:当黑色对象插入新的白色对象引用关系就将这个新的插入记录下来,等并发扫描完成之后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。

    原始快照:当灰色对象要删除指向白色对象的引用关系时,就将这个删除的引用记录下来,等并发扫描完成之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。

    参考《深入理解Java虚拟机》 周志明

  • 相关阅读:
    转:UFLDL_Tutorial 笔记(deep learning绝佳的入门资料 )
    转:使用RNN解决NLP中序列标注问题的通用优化思路
    CTR预估中GBDT与LR融合方案
    ZOJ1157, POJ1087,UVA 753 A Plug for UNIX (最大流)
    Gentoo:startx出现Failed to load module问题
    HTTP请求和响应2:方法(Method)
    SharePoint 2013 表单认证使用ASP.Net配置工具加入用户
    理解支持向量机(四)LibSVM工具包的使用
    LeetCode 14: Longest Common Prefix
    精通Hibernate——域对象之间的关系
  • 原文地址:https://www.cnblogs.com/ldsggv/p/15490305.html
Copyright © 2011-2022 走看看