深入理解JVM系列——垃圾收集算法以及HotSpot的算法实现细节

假说: 绝大多数对象朝生息灭,经历过越多次垃圾收集的对象越难被清除。按照该理论,Java堆至少被分为新生代和老年代。新生代可能被老年代所引用,跨代 引用比例极低,并且跨代引用会使新生代倾向于老年代发展,然后跨代引用就不存在了。为了记录 哪里存在跨代引用,需在新生代上建立一个全局的数据结构(记忆集),会标示出存在跨代引用的 那一小块老年代,在Minor GC中,只有有标识的小块内存才会被加入GC roots进行扫描。G1收集 器会有Mixed GC,收集整个新生代和部分老年代。

标记—清除算法: 分为标记和清除两个阶段。缺点是不稳定(大量回收情况) 和空间碎片化问题(大量不连续内存)。

标记—复制算法: 将内存划为一半,清除时将存活的对象复制到另一半,把已 使用的全清楚,不会有空间碎片化问题,但是浪费了一半内存空间。

标记—整理算法: 让存活的对象向一端移动,直接清理剩余的部分,但是移动 过程中会有STW。移动对象则内存回收时会更复杂,不移动则内存分配时会更复杂。从整个程序的 吞吐量来看,移动对象会更划算,虽然停顿时间更⻓。

HotSpot的算法实现细节

根节点枚举: 根节点枚举中也要暂停用户线程,在HotSpot虚拟机上,使用一组叫OopMap的数据结构来得到哪些地方存在对象引用,不需要去遍历所有方法区等GC roots。

安全点: 只在特定的位置去生存对应的OopMap,避免耗费大量额外的存储空 间,这些位置叫做安全点。安全点不能太多也太少,要平衡收集器等待时间和内存负荷。而线程如 何到安全点有两种策略:抢断式和主动式。第一种是暂停看哪个线程还没到,再让他跑到安全点; 第二种是运行过程不断轮询一个标志位,到了安全点则线程挂起。轮询操作很频繁,HotSpot中, 仅需要通过一条汇编指令就可以完成安全点轮询和触发线程中断了。

安全区域: 由于程序可能不在执行,无法响应请求,就引入了安全区域的,在 这个区域,引用关系不会发生变化。在这个区域中,等待直到根节点枚举完成,然后退出安全区 域。

记忆集与卡表: 记忆集是一种用于记录从非收集区域指向收集区域的指针集合 的抽象数据结构。一般使用卡表这种方式来实现记忆集,其中每个记录精确到一块内存区域,该区 域内有对象含有跨代指针。CARD_TABLE中每一个元素都对应的标识的内存区域中的一块特定大小 的内存块被称为卡⻚。只要卡⻚内有一个或多个对象的字段存在着跨代指针,那就将对应卡表的数 组元素的值标识为1,称为这个元素变脏,没有则标识为0。垃圾回收时筛选出变脏的元素然后加入 GC roots进行扫描。

写屏障: 写屏障来维护卡表的状态,可以看做是对引用类型字段赋值这个动作 的一个AOP切面,产生一个环形通知,程序可以执行额外的动作,所以赋值的前后都在写屏障的范 围内。也分为写前屏障和写后屏障。在G1出现前,都是用的写后屏障,比如赋值完成然后完成卡表 状态的更新。注意高并发情况下的伪共享问题,通过检查卡表是否被标记再将其进行变脏操作避 免。

并发的可达性分析: 并发会产生对象消失的问题,当且仅当以下两个条件同时满足:
赋值器插入了一条或多条从黑色对象到白色对象的新引用; ·赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。 要解决这个问题,增量更新可以破坏第一个条件:插入新的指向白色的引用时,将这个引用记录下来,等并发扫描结束之后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。 原始快照可以破坏第二个条件:当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。

你可能感兴趣的:(jvm,算法,java)