JVM入门级个人总结(四)垃圾回收(二)垃圾回收器

JVM个人总结(四)

(个人对黑马程序员JVM完整教程总结,原视频地址https://www.bilibili.com/video/BV1yE411Z7AP)

垃圾回收

相关VM参数

含义参数
堆初始大小-Xms
堆最大大小-Xmx 或 -XX:MaxHeapSize=size
新生代大小-Xmn或(-XX:NewSize=size + -XX:MaxNewSize=size)
幸存区比例(动态)-XX:InitialSurvivorRatio=ratio和-XX:+UseAdaptiveSizePolicy
幸存区比例-XX:SurvivorRatio=ratio
晋升阈值-XX:MaxTenuringThreshold=threshold
晋升详情-XX:+PrintTenuringDistribution
GC详情-XX:+PrintGCDetails -verbose:gc
FullGC 前 MinorGC-XX:+ScavengeBeforeFullGC

垃圾回收器

  1. 串行

    单线程

    堆内存较小,适合个人电脑

  2. 吞吐量优先

    多线程

    堆内存较大,多核CPU

    让单位时间内,STW(stop the world)的时间最短

  3. 响应时间优先

    多线程

    堆内存较大,多核CPU

    尽可能让STW的 单次 时间最短

串行

-XX:+UseSerialGC = Serial + SerialOld
在这里插入图片描述垃圾回收时stop the world 其他线程会阻塞

吞吐量优先

在这里插入图片描述
①UseParallelGC:并行(parallel,多个垃圾回收器可以同时运行,但是不能和用户工作线程同时运行) 新生代

​ UseParallelOldGC:并行 老年 标记整理 (这两个开关两个连带开启)

②采用自适应的大小调整策略(动态去调整 伊甸园和幸存区比例、堆大小、晋升阈值等等)

③调整吞吐量的大小(例:垃圾回收时间不能超过总时间的%,如果不能达到目标,就会调整堆大小,致使垃圾回收次数下降,达到垃圾 回收时间下降,吞吐量提高)
(③④两者冲突,类似成反比)

④最大垃圾回收的暂停毫秒数

⑤控制垃圾回收线程数

响应时间优先 CMS

在这里插入图片描述
①UseConcMarkSweepGC(CMS): 并发(concurrent,垃圾回收在回收的同时其他的用户线程也能同时运行) 标记清除 老年代

​ UseParNewGC: 复制 新生代

​ SerialOld:(当CMS并发失败时(碎片过多造成空间不足),会退化到SerialOld,垃圾回收时间会飙升)标记整理 单线程 老年代

②初始标记时

​ ParallelGCThreads:并行垃圾回收线程数

​ ConcGCThreads:并发垃圾回收线程数

③执行CMS垃圾回收的内存占比(预留空间给其他)

④做一次新生代垃圾回收,新生代数量少了,之后扫描对象少了,能够减轻重新标记时的压力

G1

Garbage First

JDK7中正式支持

JDK9中默认使用(JDK9中废弃了CMS)

适用场景
  • 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是200ms
  • 超大堆内存,会将堆划分为多个大小相等的Region
  • 整体上是 标记+整理算法,两个区域之间是复制算法
相关JVM参数

-XX:+UseG1GC

-XX:G1HeapRegionSize=size

-XX:MaxGCPauseMillis=time

G1垃圾回收阶段

在这里插入图片描述

1)新生代收集Young Collection

整个UI内存被划分成一个个大小相同的区域,每个区域都可以独立作为 伊甸园,幸存区,老年代
在这里插入图片描述
在这里插入图片描述

2)新生代收集+并发标记 Young Collection + CM(Concurrent Mark)
  • 在 Young GC 时会进行GC Root 初始标记
  • 老年代占用堆空间比例达到阈值时,进行并发标记(不会STW),由JVM参数决定(-XX:InitiatingHeapOccupancyPercent=percent(默认45%))
    在这里插入图片描述
3)混合收集 Mixed Collection

会对 E,S,O 进行全面垃圾回收

  • 最终标记(Remark)会STW(标记在并发标记中遗漏的)

  • 拷贝存活(Evacuate)会STW

    -XX:MaxGCPauseMillis=ms
    在这里插入图片描述

G1 会根据最大暂停时间,有选择的进行回收老年代(堆内存太大,老年代回收时间过长,所以只选择性回收垃圾多的区域)

Full GC 问题
  • Serial GC 串行

    新生代内存不足发生的垃圾收集 - minor gc

    老年代内存不足发生的垃圾收集 - full gc

  • ParallelGC 并行

    新生代内存不足发生的垃圾收集 - minor gc

    老年代内存不足发生的垃圾收集 - full gc

  • CMS

    新生代内存不足发生的垃圾收集 - minor gc

    老年代内存不足,并发失败后,退化到串行,才会有Full GC

  • G1

    新生代内存不足发生的垃圾收集 - minor gc

    老年代内存不足,在并发标记和混合收集时,如果 回收垃圾速度 跟不上 新垃圾产生的速度,并发收集失败,退化至串行收集,才会发生Full GC

Young Collection 跨代引用

新生代回收的跨代引用(老年代 引用 新生代 )问题

因为大部分 根对象 来源于 老年代,由于老年代存活的对象多,查找麻烦,所以将老年代再次细分成 CardTable卡表 (每个card 512B)
在这里插入图片描述

为了查到 新生代 的 根对象 (来源于老年代)

(例如:集合插入新的对象,集合在老年代,新的对象在新生代)

标记脏卡:将 有老年代引用新生代 的 卡 标记为脏卡

能够使在遍历根对象时,只需要遍历脏卡对象,减少搜索范围,大大提升效率
在这里插入图片描述

被引用的新生代中会有 Remembered Set 会记录 外部 对其的引用(记录对其引用的脏卡,就可以直接遍历其脏卡中的GC Root,提高效率)

concurrent refinement threads 更新 Remembered Set

在 引用 变更 时通过post-write barrier + dirty card queue,将脏卡的更新 信息保存在此queue中之后再由一条线程去更新

Remark 重新标记阶段

以下是并发标记中的处理阶段 :

黑色:已经处理完成的,结束时会存活

灰色:正在处理当中的,因为被强引用,最终会变为黑色存活

白色:未处理的,因为被强引用,最终也会变成黑色存活
在这里插入图片描述

由于该标记的环境是 并发
在这里插入图片描述

所以,Remark 重新标记 就是解决这个问题

加入 写屏障:当C对象的 引用 发生改变时,写屏障代码就会执行,将C加入到一个队列中,并变为灰色

在并发标记阶段结束后,再去判断该队列中的对象是否有被引用,再处理

JDK 8u20 字符串去重

优点:节省大量内存

缺点:略微多占用了cpu时间,新生代回收时间略微增加

-XX:+UseStringDeduplication
在这里插入图片描述

  • 将所有新分配的字符串放入一个队列

  • 当新生代回收时,G1并发检查是否有字符串重复

  • 如果他们值一样,让它们引用同一个char[]

  • 注意:与String.intern()不一样

    ​ String.intern()关注的是字符串对象

    ​ 而字符串去重关注的是char[]

    ​ 在JVM内部,使用了不同的字符串表

JDK 8u40 并发标记类卸载
  • 所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它加载的所有类(只有自定义的会被卸载)

-XX:+ClassUnloadingWithConcurrentMark 默认启动

JDK 8u60 回收巨型对象
  • 一个对象大于region的一半时,称之为巨型对象
  • G1 不会对巨型对象进行拷贝
  • 回收时被优先考虑
  • G1会跟踪老年代所有incoming引用,这样老年代incoming引用为0的巨型对象就可以在新生代垃圾回收时处理掉(例如下图中①②引用没有后,该H就可以在新生代垃圾回收时处理)
    在这里插入图片描述
JDK 9 并发标记起始时间的调整
  • 并发标记 必须在堆空间沾满前完成,否则退化为 FullGC

  • JDK 9 之前需要使用 -XX:InitiatingHeapOccupancyPercent

  • JDK 9 可以动态调整

    -XX:InitiatingHeapOccupancyPercent 用来设置初始值

    进行数据采样并动态调整

    总会添加一个安全的空档空间

g引用为0的巨型对象就可以在新生代垃圾回收时处理掉(例如下图中①②引用没有后,该H就可以在新生代垃圾回收时处理)

[外链图片转存中…(img-UQMBnaQ0-1639831561616)]

JDK 9 并发标记起始时间的调整
  • 并发标记 必须在堆空间沾满前完成,否则退化为 FullGC

  • JDK 9 之前需要使用 -XX:InitiatingHeapOccupancyPercent

  • JDK 9 可以动态调整

    -XX:InitiatingHeapOccupancyPercent 用来设置初始值

    进行数据采样并动态调整

    总会添加一个安全的空档空间

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值