GC垃圾回收器【入门笔记】

GC:Garbage Collectors 垃圾回收器

C/C++,手动回收内存;难调试、门槛高。忘记回收、多次回收等问题

Java、Golang等,有垃圾回收器:自动回收,技术门槛降低

一、如何定位垃圾?

https://www.infoq.cn/article/3wyretkqrhivtw4frmr3

https://juejin.cn/post/7123853933801373733

1、引用计数法

无法解决循环调用的问题

2、根可达分析算法

一般需要STW(stop-the-world)

GC垃圾回收器【入门笔记】_第1张图片

GC垃圾回收器【入门笔记】_第2张图片

二、哪些内存需要回收?

运行期间动态创建的、内存分配和回收具有不确定性的:堆、常量池(方法区)

GC垃圾回收器【入门笔记】_第3张图片

三、常见的GC算法

标记清除算法、标记复制法、标记压缩法、分代收集算法

1、mark-sweep 标记清除法(三色标记法)

位置不连续 产生碎片 效率偏低(两遍扫描)

黑色区域表示待清理的垃圾对象,标记出来后直接清空。该方法简单快速,但是缺点也很明显,会产生很多内存碎片。

GC垃圾回收器【入门笔记】_第4张图片

GC垃圾回收器【入门笔记】_第5张图片

2、mark-copy 标记复制法

没有碎片,但浪费空间(只能使用50%的内存)

将内存对半分,总是保留一块空着(上图中的右侧),将左侧存活的对象(浅灰色区域)复制到右侧,然后左侧全部清空。避免了内存碎片问题,但是内存浪费很严重,相当于只能使用 50%的内存。

GC垃圾回收器【入门笔记】_第6张图片

3、mark-compact 标记压缩法

没有碎片,效率偏低(两遍扫描,指针需要调整)

GC垃圾回收器【入门笔记】_第7张图片

避免了上述两种算法的缺点,将垃圾对象清理掉后,同时将剩下的存活对象进行整理挪动(类似于 windows 的磁盘碎片整理),保证它们占用的空间连续,这样就避免了内存碎片问题,但是整理过程也会降低 GC 的效率。

GC垃圾回收器【入门笔记】_第8张图片

4、generation-collect 分代收集算法

综合使用了标记清除、标记复制、标记压缩算法。堆内存逻辑分区:新生代(Young Generation)、老年代(Old Generation),Young Genaration 更是又细为分 eden,S0,S1 三个区。

YGC/FGC过程:

  1. 产生的新对象,放eden区
  2. 当 eden 区放不下时,就会发生 minor GC(也被称为 young GC):标记不可达对象,然后将可达对象移动到S0区,清空eden区(综合运用了“【标记-清理 eden】 + 【标记-复制 eden->s0】”算法)
  3. eden 如果又满了,再次触发 minor GC:先做标记,s0 区和 eden 区的存活对象,将直接搬到 s1 区。然后将 eden 和 s0 区的垃圾清理掉
  4. 对象在年青代的 3 个区(edge,s0,s1)之间,每次从 1 个区移到另 1 区,年龄+1,在 young 区达到一定的年龄阈值后,将晋升到老年代
  5. 如果老年代,最终也放满了,就会发生 major GC(即 Full GC),由于老年代的的对象通常会比较多,因为【标记-清理-整理(压缩)】的耗时通常会比较长,会让应用出现卡顿的现象,这也是为什么很多应用要优化,尽量避免或减少 Full GC 的原因。

GC垃圾回收器【入门笔记】_第9张图片

JVM调优:参数指定内存大小

jdk8 开始,用 MetaSpace 区取代了 Perm 区(永久代),所以相应的 jvm 参数变成-XX:MetaspaceSize 及 -XX:MaxMetaspaceSize。

GC垃圾回收器【入门笔记】_第10张图片

四、常见的GC

1、GC的演变过程(内存不断扩大)

常见组合:Serial+Serial Old【单线程】、PS+PO【多线程】 、ParNew+CMS

垃圾回收器 使用的GC算法 说明
Serial 标记-复制 【Y】单线程、会发生STW。早期单核
Serial Old “标记-整理” 【O】单线程、STW。耗时长
Parallel Scavenge 标记-复制 【Y】ParNew 的升级版本(吞吐量优先的收集器,吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),区别在于提供了两个参数:-XX:MaxGCPauseMillis 最大垃圾回收停顿时间;-XX:GCTimeRatio 垃圾回收时间与总时间占比
Parallel Old “标记-整理” 【O】Serial Old的多线程版本,STW
ParNew 标记-复制 【Y】Serial 的多线程版本,会 STW,多核机器
CMS 标记清理,三色标记 【O】并发多线程,STW有所缩短。最大问题是会产生漏标
G1 标记-整理 不分代。并发多线程,解决了CMS的缺陷
ZGC 不分代。目前最好用的GC。
Epsilon 不执行任何垃圾回收工作。一旦java的堆被耗尽,jvm就直接关闭。特殊场景:1.开发JVM的人debug使用;2.程序运行完都用不完内存,等程序运行完直接清理所有内存。
shenandoah 开源,成熟度也比较高。

![image-20230714092649450](D:\Users\helen.zeng\AppData\Roaming\Typora\typora-user-images\image-20230714092649450.png

GC垃圾回收器【入门笔记】_第11张图片

2、Serial

蓝色是业务线程、黄色是GC线程(暂停所有业务线程来做垃圾回收处理)

GC垃圾回收器【入门笔记】_第12张图片

3、ParNew

多线程,有STW。

GC垃圾回收器【入门笔记】_第13张图片

4、CMS

CMS采用了多种方式尽可能降低GC的暂停时间,减少用户程序停顿。
停顿时间降低的同时牺牲了CPU吞吐量 。因为并发情况占用大量cpu资源
这是在停顿时间和性能间做出的取舍,可以简单理解为"空间(性能)"换时间

三色标记算法:黑色、灰色、白色

GC垃圾回收器【入门笔记】_第14张图片

CMS:Concurrent Mark Sweep

1)Inital Mark 初始标记:主要是标记 GC Root 开始的下级(注:仅下一级)对象,这个过程会 STW,但是跟 GC Root 直接关联的下级对象不会很多,因此这个过程其实很快。

2)Concurrent Mark 并发标记:根据上一步的结果,继续向下标识所有关联的对象,直到这条链上的最尽头。这个过程是多线程的,虽然耗时理论上会比较长,但是其它工作线程并不会阻塞,没有 STW。

3)Remark 再标志:这是修正过程。为啥还要再标记一次?因为第 2 步并没有阻塞其它工作线程,其它线程在标识过程中,很有可能会产生新的垃圾。

4)Concurrent Sweep:并行清理,这里使用多线程以“Mark Sweep-标记清理”算法,把垃圾清掉,其它工作线程仍然能继续支行,不会造成卡顿。

GC垃圾回收器【入门笔记】_第15张图片

GC垃圾回收器【入门笔记】_第16张图片

5、G1:Garbage-First

G1最大的特点是引入分区的思路,弱化了分代的概念。物理不分代、逻辑分代。G1也不需要跟别的收集器一起配合使用,自己就可以搞定所有内存区域。

毛病:一次回收要把年轻代全回收完。

GC垃圾回收器【入门笔记】_第17张图片

6、ZGC:

分页算法,完全不分代了,而且分区非常灵活。LoadBarrier(读屏障)。把比较满的分区做回收

G1算法通过只回收部分Region,避免了全堆扫描,改善了大堆下的停顿时间

7、Shenandoah

ColoredPointers + LoadBarrier(颜色指针算法+读屏障)

程序中最难调试的bug

  • 野指针(不再指向任何对象的指针):同一个对象有两个指针,一个释放了,另一个不知道(还在使用),NPE
  • 并发问题:多线程访问同一块内存空间

你可能感兴趣的:(性能测试,GC,垃圾回收器,YGC,fgc,三色标记法)