JVM学习日记⭐️HotSpot算法细节实现(上)⭐️🔉引

“这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战

🔉引言

前面说了对象判决垃圾收集算法,接下来说说HotSpot算法细节的实现,算法算法,效率第一,才能保证虚拟机的高效运行。

根节点枚举

不要怕,根节点枚举,对于专有名词一律用大白话处理,首先说说根节点,我们哪里见过根节点啊?嗯?可能大家忘了,我们在对象判决中提到过根节点传送门】,就是GCRoots,枚举呢就是遍历查找,那我们之前就提到过一个场景,可达性分析,我们要查找这个对象是否是垃圾对象,就看它是否在GCRoots的引用链上,但是你要是挨个检查,无疑会浪费不少时间。

而且根节点枚举这一步骤是必须暂停所有的用户线程的,也就是我们常说的Stop the Wrold,那为什么执行这一操作需要暂停所有用户线程呢?到底是谁在影响可达性分析结果的准确性呢?我们来思考一下:那就想想如果我们不暂停用户线程,会发生什么?可能对象之间的引用频繁会发生变化,这样,我们就无法保证可达性分析的准确性了,因为可能这一时刻是垃圾,下一刻就不是啦啊,所以我们必须保证根节点枚举这一过程执行时,引用关系必须要冻结。

那当用户线程停顿下来之后,我们也不需要一个不漏的检查完所有执行上下文和所有引用的位置,虚拟机这么聪明,肯定是能直接得到我们的对象引用的,所以我们要吊打🔨虚拟机(邪恶😈),据虚拟机交代:是有一组称为:OopMap的数据结构储存的,在类加载动作完成之后,虚拟机就会把什么偏移量上是什么类型给计算出来,并进行存储,那这样收集器在扫描时就可直接得到引用信息了。

安全点

OopMap的帮助下,Hotspot会快速准确的完成GCRoots枚举,那问题来了,如果为每个指令都进行存储,空间有点顶不住啊💥,这样垃圾回收带来的成本将是几何倍的增加。那虚拟机撒谎了嘛?又一顿严刑拷打,虚拟机哭了:我没有让OopMap都存储啊,我只是在特定位置上进行存储,这样的位置称为“安全点”。

于是,我们找到了安全点,安全点说:老大交代了,用户程序在执行垃圾回收时,代码指令流必须要到达我这,才能暂停进行垃圾收集,(你这不就是卡点嘛)。接下来问了问收集器,收集器说:“这样的卡点不能太多,太多我等待时间长,太少。我执行次数太频繁,会增大运行时内存的负荷。

那啥样的地方会产生安全点呢?当然是繁华的地方,繁华的地方路特别多,程序跑的时间也特别长,”长时间执行“最明显的特征就是指令的复用,比如:方法调用、循环跳转、异常跳转等。

那我们知道,java是多线程的啊,那我怎么能让这么多线程在垃圾回收的时候都跑到最近的安全点呢?这里我们有两个方法:抢先式中断Preemptive Suspension)和主动式中断Voluntary Suspension)。

线程中断方案

抢先式中断

当垃圾收集需要中断线程时,由系统把所有线程先中断,说白了就是断电,然后看看谁不在安全点上,恢复这个线程执行,再过一段时间中断,直到跑到安全点上。

主动式中断

当垃圾收集需要中断线程时,不对线程进行操作,只是简单设置一个标志位,交给所有线程主动去轮询,当发现结果为true时,就自己在最近的安全点上主动挂起,轮询操作的指令必须足够精简,因为轮询操作会在代码频繁出现,这样才能保证它足够高效。在HotSpot中,该操作采用内存保护陷阱的方式,将汇编指令精简到一条。

安全区域

前面我们提到,线程执行可以自己跑到安全点,要是线程不执行呢?嗯?线程还有不执行的时候?线程睡着了吗??对,就是睡着了😴,那虚拟机还要等它醒?重新分配处理器时间吗?那显然是不可能的,虚拟机一脸拒绝,那我们就需要引入安全区域(Safe Region)来帮我们搞定了。

点动成线,线动成面安全点拉伸自然会组成安全区域,该区域能够确保在一段代码片段之中的引用关系不会发生变化。

就是说线程如果开进了这块区域,就会被标识已进入该区域,虚拟机在进行根节点枚举的操作时,就必须一直等待,直到收到该操作执行完的信号时方可离开。

📢题外话

😩我发现我哪有锻炼的时间呢?

是黑洞时间太多了嘛?其实也不是,每天20:00左右到家,吃饭,娱乐一小会,然后就写文章写到快22:50左右,然后就23:00,还锻炼个鬼啊?

就和健身一样,时间记录也是一件蛮难的事情,从我开始记录以来,好像都没有坚持到一周过,我原来是咋记录的呢?就是每天简要的记一下自己做事用的时间,表格记录着:上班,看书,写作,玩,第二天还是上班,看书,写作,玩,我感觉太千篇一律,无聊透顶。

是我把时间记录想的太简单了,序言提到:时间记录是一个复杂而完整的体系,需要有清晰的记录原则,恰当的时间分类,简单的记录行为,还有对记录效率的思考和对时间如何分析利用的深度探索。

没深入看,就先当自己学会了,在实践中慢慢探索吧,就这么着,明个儿再看😂。