JVM(五)-垃圾回收器及HotSpot中的细节实现

2 篇文章 0 订阅

垃圾回收器及HotSpot中的细节实现

并发标记

三色标记

  在三色标记之前有一个算法叫Mark-And-Sweep(标记清除).这个算法会设置一个标记位来记录对象是否被使用.最开始所有的标记位都是0.如果发现对象是可达的就会置为1,一步步下去就会呈现一个类似树状的结果.等标记的步骤完成后,会将未标记的对象统一清理,再次把所有的标记位设置成0方便下次清理.
  这个算法最大的问题是GC 执行期间需要把整个程序完全暂停,不能异步进行GC操作.因为在不同的阶段标记清除法的标志位0和1有不同的含义,那么新增的对象无论标记什么都有可能意外删除这个对象.对实时性要求高的系统来说,这种需要长时间挂起的标记清除法是可接受的.所以就需要一个算法来解决GC运行时程序长时间挂起的问题,那就是三色标记法.
  三色标记最大的好处是可以异步执行,从而可以 以中断时间极少的代价或完全没有中断来进行整个GC.
  三色标记法很简单,首先将对象用三种颜色表示,分别是白色,灰色和黑色
黑色: 根对象,或者该对象与它的子对象都被扫描过
灰色: 对本身被扫描,但是还没扫描完该对象的子对象
白色: 未被扫描对象,如果扫描完所以对象之后,最终为白色的为不可达对象,既垃圾对象.
在这里插入图片描述

三色标记的问题

GC并发情况下的漏标问题
1.线程1完成所有的标记,线程2还处于半完成状态
在这里插入图片描述
2.引用发生以下改变:
A.c = C
B.c = null
在这里插入图片描述
3. 线程1,2完成所有的标记 c对象未被扫描到(因为线程1在C对象引用没发生改变之前已经完成扫描) 依旧是白色 被错误回收

CMS中的解决方案

Incremental Update算法
  当一个白色对象被一个黑色对象引用,将黑色对象重新标记为灰色,让垃圾回收器重新扫描

G1中的解决方案

STAB(snapshot-at-the-beginning)
刚开始做一个快照,当B和C消失的时候把这个引用到GC的堆栈,保证C还能被GC扫描到,最重要的是要把这个引用推到GC的堆栈,是灰色对象指向白色的引用,如果一旦某一个引用消失掉了,我会把它放到栈(GC方法运行时数据也是来自栈中),我其实还是能找到它的,我下回直接扫描他就行了,那样白色就不会漏标
最终标记

对用户线程做另一个短暂的暂停,用于处理并发阶段结后仍遗留下来的最后那少量的STAB记录(漏标对象)

对比

SATB算法是关注引用的删除(B->C的引用)
Incremental Update算法关注引用的增加.(A-C引用)
G1如果使用Incremental Update算法,因为变成灰色的成员还要重新扫,效率太低了.
所以G1在处理并发标记的过程比CMS效率要高,这个主要是解决漏标的算法决定的

G1中的技术细节

在这里插入图片描述

跨带引用

&emso; 堆空间通常被划分为新生代和老年代,由于新生代的垃圾收集通常很频繁,如果老年代对象引用了新生代的对象,那么回收新生代的话,需要跟踪从老年代到新生代的所有引用,所以要避免每次YGC时扫描整个老年代,减少开销

RSet(记忆集)

  记录了其他Region中的对象到本Region的引用,
  RSet的价值在于使得垃圾收集器不需要扫描整个堆,找到谁引用了当前分区中的对象,只需要扫描RSet即可.
  RSet本身就是一个Hash表,如果是在G1的话,则是在一个Region区里面

CardTable

  由于做新生代GC时,需要扫描整个OLD区,效率非常低,所以JVM设计了CardTable,如果一个OLD区CardTable中有对象指向Y区,就将它设为Dirty(标志位1),下次扫描时,只需要扫描CardTable上是Dirty的内存区域即可
  字节数组CardTable的每一个元素都对应着其标识的内存区域中一块特定大小的内存块,这个内存块被称作"卡页"(Card Page).一般来说,卡页大小都是以2的N次幂的字节数,假设使用的卡页是2的10次幂,即1M,内存区域的起始地址是0X0000的话,数组CARD_TABLE的第0,1,2号元素,分别对应地址范围为0X0000-0x03FF,0x0400-0x7FF,0x800-0x011FF的卡页内存

总结

  这里描述的是G1处理跨带引用的细节,其实在CMS中也有类似的处理方式,比如CardTable,也需要记录一个Rset来记录,我们对比一下,在G1中是每一个Region都需要一个Rest的内存区域,导致G1的Rset可能会占据整个堆容量的20%乃至更多.但是CMS只需要一份,所以就内存占用来说,G1占用的内存需求更大,虽然G1的优点很多,但是我们不推荐在堆空间比较小的情况下使用G1,尤其小于6个G

安全点与安全区域

安全点

  用户线程暂停,GC线程要开始工作,但是要确保用户暂停的这行字节码指令是不会导致引用关系的变化.所以JVM会在字节码指令中,选一些指令,作为"安全点",比如方法调用,循环跳转,异常跳转等,一般是这些指令才会产生安全点.
  为什么它叫安全点,是这样的,GC时要暂停业务线程,并不是抢占式中断(立马把业务线程中断)而是主动式中断.
  主动式中断是设置一个标志,这个标志是中断标志,各业务线程在运行过程中会不停的主动去轮询这个标志,一旦发现中断标志为True,就会在自己最近的"安全点"上主动中断挂起.

安全区域

为什么需要安全区域?
  要是业务线程都不执行(业务线程处于Sleep或者是Blocked状态),那么程序就没办法进入安全点,对于这种情况,就必须引入安全区域.
  安全区域是指能够确保在某一段代码之中,引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾收集都是安全的.我们也可以把安全区域看做被扩展拉伸了的安全点
在这里插入图片描述
  当用户线程执行到安全区域里面的代码时,首先会标识自己已经进入了安全区域,这段时间里JVM要发起GC就必须去管这个线程了.
  当线程要离开安全区域时,查看JVM是否已经完成了(根节点枚举或者其他GC中需要暂停用户线程的阶段)
1丶 如果完成了,那线程就当做没事发生过,继续执行
2丶是否她就必须一直等待,直到收到可以离开安全区域的信号为止.

低延迟的垃圾回收器

垃圾回收器三项指标

  传统的垃圾回收器一般情况下内存占用,吞吐量,延时 同时只能满足两个,但是现在的发展,延迟这项的目标越来越重要.所以就有低延迟的垃圾回收器

Eplison(了解即可)

  这个垃圾回收器不能进行垃圾回收,是一个"不干活"的垃圾回收器,由于RedHat退出,它还要负责堆的管理与布局,对象的分配,与解释器的协作,与编译器的协作,与监控子系统协作等职责,主要用于需要剥离垃圾收集器影响的性能测试和压力测试

ZGC(了解即可)

  有类似于G1的Region,但是没有分代
  标志性的设计是染色指针ColoredPointers(这个概念了解即可),染色指针有4TB的内存限制,但是效率极高,它是一种将少量额外的信息存储在指针上的技术
  它可以做到几乎整个收集过程全程可并发,短暂的STW也只与GC ROOTS大小相关而与堆内存空间大小无关,因此它可以实现任何堆空间STW的时间小于十毫秒的目标

Shenandoah(了解即可)

  第一款非 Oracle 公司开发的垃圾回收器,有类似于 G1 的 Region,但是没有分代。 也用到了染色指针 ColoredPointers。 效率没有 ZGC 高,大概几十毫秒的目标。

GC参数

GC日志详解

在这里插入图片描述

GC常用参数

-Xmn -Xms -Xmx –Xss 年轻代 最小堆 最大堆 栈空间
-XX:+UseTLAB 使用 TLAB,默认打开
-XX:+PrintTLAB 打印 TLAB 的使用情况
-XX:TLABSize 设置 TLAB 大小
XX:+DisableExplicitGC 启用用于禁用对的调用处理的选项 System.gc()
-XX:+PrintGC 查看 GC 基本信息
-XX:+PrintGCDetails 查看 GC 详细信息
-XX:+PrintHeapAtGC 每次一次 GC 后,都打印堆信息
-XX:+PrintGCTimeStamps 启用在每个 GC 上打印时间戳的功能
-XX:+PrintGCApplicationConcurrentTime 打印应用程序时间(低)
-XX:+PrintGCApplicationStoppedTime 打印暂停时长(低)
-XX:+PrintReferenceGC 记录回收了多少种不同引用类型的引用(重要性低)
-verbose:class 类加载详细过程
-XX:+PrintVMOptions 可在程序运行时,打印虚拟机接受到的命令行显示参数
-XX:+PrintFlagsInitial 打印所有的 JVM 参数、查看所有 JVM 参数启动的初始值(必须会用)
-XX:MaxTenuringThreshold 升代年龄,最大值 15, 并行(吞吐量)收集器的默认值为 15,而 CMS 收集器的默认值为 6。

Parallel常用参数

-XX:SurvivorRatio 设置伊甸园空间大小与幸存者空间大小之间的比率。默认情况下,此选项设置为 8
-XX:PreTenureSizeThreshold 大对象到底多大,大于这个值的参数直接在老年代分配
-XX:MaxTenuringThreshold 升代年龄,最大值 15, 并行(吞吐量)收集器的默认值为 15,而 CMS 收集器的默认值为 6。
-XX:+ParallelGCThreads 并行收集器的线程数,同样适用于 CMS,一般设为和 CPU 核数相同
-XX:+UseAdaptiveSizePolicy 自动选择各区大小比例

CMS常用参数

-XX:+UseConcMarkSweepGC 启用 CMS 垃圾回收器
-XX:+ParallelGCThreads 并行收集器的线程数,同样适用于 CMS,一般设为和 CPU 核数相同
-XX:CMSInitiatingOccupancyFraction 使用多少比例的老年代后开始 CMS 收集,默认是 68%(近似值),如果频繁发生 SerialOld 卡顿,应该调小,(频繁 CMS 回 收)
-XX:+UseCMSCompactAtFullCollection 在 FGC 时进行压缩
-XX:CMSFullGCsBeforeCompaction 多少次 FGC 之后进行压缩
-XX:+CMSClassUnloadingEnabled 使用并发标记扫描(CMS)垃圾收集器时,启用类卸载。默认情况下启用此选项。
-XX:CMSInitiatingPermOccupancyFraction 达到什么比例时进行 Perm 回收,JDK 8 中不推荐使用此选项,不能替代。
-XX:GCTimeRatio 设置 GC 时间占用程序运行时间的百分比(不推荐使用)
-XX:MaxGCPauseMillis 停顿时间,是一个建议时间,GC 会尝试用各种手段达到这个时间,比如减小年轻代

G1常用参数

-XX:+UseG1GC 启用 CMS 垃圾收集器
-XX:MaxGCPauseMillis 设置最大 GC 暂停时间的目标(以毫秒为单位)。这是一个软目标,并且 JVM 将尽最大的努力(G1 会尝试调整 Young 区的块数来)来实 现它。默认情况下,没有最大暂停时间值。
-XX:GCPauseIntervalMillis GC 的间隔时间
-XX:+G1HeapRegionSize 分区大小,建议逐渐增大该值,1 2 4 8 16 32。随着 size 增加,垃圾的存活时间更长,GC 间隔更长,但每次 GC 的时间也会更长
-XX:G1NewSizePercent 新生代最小比例,默认为 5%
-XX:G1MaxNewSizePercent 新生代最大比例,默认为 60%
-XX:GCTimeRatioGC 时间建议比例,G1 会根据这个值调整堆空间
-XX:ConcGCThreads 线程数量
-XX:InitiatingHeapOccupancyPercent 启动 G1 的堆空间占用比例,根据整个堆的占用而触发并发 GC 周期

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值