二 JVM.垃圾回收算法/策略内存分配( 五 )


3.建立可靠停顿预测模型: -XX: 来设置 ,但是G1如何保证;为每一个计算出一个衰减均值,在垃圾回收过程中,会评估记录每个的回收耗时,Set 里面的脏卡数量;衰减均值不同于均值,最近的数据影响也是比较大;
GC步骤
1.初始标记:,标记GC roots 关联的对象,设置TAMS ,STD 时间非常短暂
2.并发标记:从GC roots开始对堆中对象可达性分析,时间较长,但是可以和用户线程并发执行;分析完成后,还需要重新处理SATB并发修改的对象引用;
3.最终标记:Final对用户线程做一个短暂的暂停,处理少量的SATB
4.筛选回收:Live Dateand; 记录统计计算,回收价值排序,根据用户设置的参数,来选择多个区域回收;把回收区域的存活对象复制到空的中去,其中涉及对象引用的修改,必须停止用户线程;
用户根据设置停顿时间来在不同的应用场景中达到吞吐量和低延迟之间的平衡,但是期望停顿时间也是不能设置的太短,一般是一百多到三百多是比较适当的参数;不然回收失败还是会导致Full GC
G1的目标是取代CMS ; G1优点:G1总体是标记-整理;但是在两个之间又是标记-复制,带来的好处就是完整的内存区域,不产生内存碎片;缺点:负载高,维护的卡表内存可能会占据内存的20%;维护卡表的操作G1更加复杂;
G1更适合大内存的应用;CMS 适合小内存;两者性能的分界点在6~8G 之间;
6.低延迟垃圾回收器
回顾垃圾回收器发展史:衡量一个垃圾回收器的好坏有下面三个重要的指标
现在的硬件发展,内存越来越大,对延迟的要求越来越凸显
6.1
公司发展;官方没有支持;
目标:任何堆内存大小,垃圾回收停顿时间在10毫秒以内;
相比于 官方的ZGC ,更像是G1的下一代继承者,相似的内存布局,和处理步骤,甚至共用一部分代码; 的改进有如下:
回收步骤:
转发指针:在转移对象和用户线程并发的一种解决方案;在移动前的内存上设置保护陷阱;一旦访问到旧的内存地址就会自陷中断,进入异常处理,再有代码逻辑修改访问到新的地址 。但是这样,需要操作系统支持,并且需要频繁切换到核心态,代价较大;
新方案:优点:不用设置保护陷阱;而是在原有的内存布局结构前面加上新的引用字段,正常不处于并发移动的情况下,引用指向自己;
比如多个对象引用一个对象A,原有的方式就是,只要是对象A移动的位置,所有引用到A 的对象指针地址都需要修改成新的地址;那么新的数据结构就是 对象指针地址是两部分组成,一个是,一个是原有的 ;在并发移动后,只需要把,只需要修改一处,就可以了;(类似于使用句柄定位对象,只是转发指针是分散在对象上面)
缺点是:每次访问对象都有一次指针转发,尽管这个开销已经被优化到一行汇编指令;
并发情况的处理,2 如果在1,3 之间的话就会出现问题,通过 CAS 操作保证其中之一执行,另一个必须等待;
1.GC 线程复制对象副本
2.用户线程更新对象某个字段
3.GC 线程更新转发指针的引用值为新副本地址;
写屏障维护记忆矩阵,读屏障指针的转发处理;尤其是读屏障的使用,因为读的频率很高,所以必须谨慎使用,后续在JDK13 是考虑为引用访问屏障,而不去管原生数据类型的访问
6.2 ZGC
公司研发; **目标:**和上面的 一样;
在X64 平台下 (Zpage) 有大中小三种容量;
ZGC 的思路和完全不同,设计更加复杂精巧;标志性的设计是其染色指针技术;以往我们要在对象上存储额外的信息,比如分代年龄,哈希码,锁等都是存储在对象头上面 。正常情况下是可以流程访问,但是在并发移动情况下就会有额外的负担,又或者是不去访问对象数据就可以直接获取到对象的一些信息呢?比如是否被移动;就比如之前垃圾回收的三色标记,像 是直接在对象头上面标记的,像G1 和就把这个数据记录在 上,这里,ZGC 是最直接的,最纯粹的; 原来是遍历对象图,现在就是遍历对象图的引用(染色指针)