JVM系列垃圾回收机制(Garbage Collect)

网友投稿 654 2022-05-30

JVM系列之垃圾回收机制(Garbage Collect)

1、前言介绍

在前面章节的学习中,我们知道了java虚拟机的运行时数据区和类加载机制,了解了在堆内存中是有垃圾回收的,比如young区的Minor GC,Old区的Major GC,young区和old区的full GC。

对于一个内存中的对象,怎么确定它需要回收的?怎么样对它进行回收?

2、如何确定一个对象需要回收?

对于引用计数法而言,只要应用程序中持有对该对象的引用,则这个对象不需要回收,如果这个对象没有任何指针对其引用,则这个对象需要回收。

弊端:如果对象A和B之间相互持有引用,会导致永远不会被回收

写个例子进行验证:

public class TestGc { static class A{ public B b; } static class B { public A a; } public static void testGc() { A a = new A(); B b = new B(); a.b = b; b.a = a; a = null; b = null; // 强制进行gc回收 System.gc(); } public static void main(String[] args) { testGc(); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

添加java虚拟机参数-XX:+PrintGC,在控制台打印出gc日志

从日志可以看出,在jdk8里还是有进行回收的,说明jdk8默认的回收机制不是基于“引用计数法”的

[GC (System.gc()) 4301K->1033K(117760K), 0.0024682 secs] [Full GC (System.gc()) 1033K->990K(117760K), 0.0170595 secs]

1

2

JVM系列之垃圾回收机制(Garbage Collect)

通过GC Root的对象,开始向下寻找,看某个对象是否可达,能查找到就是可达

在Java技术体系里面,固定可作为GC Roots的对象有:

虚拟机栈引用的对象

方法区中类静态属性引用的对象

方法区中常量引用的对象

本地方法栈中JNI(Native方法)引用的对象

·所有被同步锁(synchronized)持有的对象

类加载器、Thread、基本数据类型对应的Class对象、常驻的异常对象

等等

3、什么时候会垃圾回收?

(1)、当Eden区或者S区不够用时

(2)、old区空间不够用时

(3)、方法区空间不够用时

(4)、System.gc() 手动回收(通知回收,什么时候回收由jvm决定,生产环境不建议使用,因为gc消耗的资源比较大)

4、 垃圾收集算法

标记Mark

找出内存中需要回收的对象,并且将它们标记出来

引用官网图片:

清除(Sweep)

清除掉被标记需要回收的对象,释放出对应的内存空间

将不需要回收的对象,如图Referenced对象复制到S0,然后清理Eden,当然Young区的垃圾回收,还有没那么简单,这里不详细描述(为什么要两个S区的原因可以找上一章学习)

同样需要标记的过程:

让所有Referenced对象都向一端移动,清理掉边界意外的内存。

5、 分代垃圾收集过程

在前面的学习中,我们知道了堆是jvm一个很重要的部分,堆可以分为young区(“新生代”)和old区(“老年代”),young区再细分为Eden区、S0区(From区)、S1区(To区),然后对象是怎么进行分代分配收集的,然后按照官网描述走一遍流程,图来自官网

1、任何新对象最开始都被分配到 eden 空间。两个幸存者空间一开始都是空的,图来自官网

2、伊甸园空间填满时,会触发一个次要的垃圾收集

3、引用的对象会被移到S0区,然后清理Eden区,未引用的对象被清理

4、在下一次的小GC中,Eden区引用的对象同样会移到幸存区,不过这次不是S0(From)区,而是S1(To)区。同时原来S0(From)区中的引用对象达到一个阈值后,也会被移到S1区,当所有符合条件的引用对象都被移到S1时,就会触发GC,清理Eden区、S0区的对象

5、在接下来的下一个次要(minor )GC,会重复同样的过程。不过这一次,幸存区被换了,这次换成S0区,Eden区引用的对象和S1中的引用对象被移到S0区,然后清理Eden区和S1区

6、前面就是young区的minor GC大概过程,当对象达到一定的年龄阈值(本例中为8)时,它们会从“年轻代“提升到“老年代“。

7、 随着次要 GC 的继续发生,对象将继续被提升到老年代空间

8、如果old区空间满了,将在old区执行一次major GC,清理并压缩该空间

6、 垃圾收集器

垃圾收集算法是方法论,垃圾收集器就是内存回收的具体实现,按照并行多线程、作用 范围(作用于“新生代”还是“老年代”),可以细分为各类的垃圾收集器

young Generation Collection:

Serial

Serial收集器是最基本,发展历史最早的收集器,在jdk1.3.1之前是java虚拟机唯一的选择,它是一种单线程的收集器,采用复制算法。

ParNew

ParNew是一种多线程版本的收集器,也是采用复制算法的收集器,可以理解为Serial的多线程版本。

Parallel Scavenge

Parallel Scavenge 也是一种复制类型的收集器,支持多线程并发,看起来和ParNew有点像,不过Parallel Scanvenge更关注系统的吞吐量。

Old Generation Collection

Serial Old

Serial Old收集器是Serial收集器的老年代版本,也是一个单线程收集器,采用的收集算法是“标记-整理”,“mark-sweep-compact”,运行过程和Serial一样

CMS

Concurrent Mark Sweep(CMS),是一种以获取 最短回收停顿时间 为目标的收集器,采用的是标记-清除算法,官方文档

整个过程分为4:

(1)、初始标记(CMS initial mark):标记GC roots直接关联对象,不需要Tracing,速度是很快的

(2)、并发标记(CMS concurrent mark):这个过程会进行GC Roots tracing

(3)、重新标记(CMS remark):重新标记并发标记中因用户程序改变的内容

(4)、并发清楚(CMS concurrent sweep):这个过程会清理不可达的对象,回收内存空间(不过这个过程会产生新垃圾,留着下次清理,这个被称之为浮动垃圾)

注意:总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的,因为整体过程,并发标记和并发清除,收集器线程可以与用户线程一起工作

Parallel Old

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,支持多线程,使用“标记-整理算法”进行垃圾回收,也是更加关注系统的吞吐量

G1

Garbage-First (G1) 收集器是一种服务器式垃圾收集器,适用于具有大内存的多处理器机器。

G1收集器,Java堆的内存布局和其它收集器有很大差别,它将整个java堆划分为多个大小相等的独立区域(Region),虽然还保留着“新生代”和“老年代”的概念,不过“新生代”和“老年代”不再是以前的设计模型,而是很大Region区域的集合。所谓Garbage-Frist,其实就是优先回收垃圾最多的Region区域。

引用官网的图例:

每个Region大小都是一样的,可以是1M到32M之间的数值,但是必须保证是2的n次幂,如果对象太大,一个Region放不下,超过了Region大小的50%,就会直接放到H中

注意:设置Region大小:-XX:G1HeapRegionSize=M

G1收集器收集过程:

初始标记(Initial Marking) 标记以下GC Roots能够关联的对象,并且修改TAMS的值,需要暂停用户线程

并发标记(Concurrent Marking) 从GC Roots进行可达性分析,找出存活的对象,与用户线程并发执行

最终标记(Final Marking) 修正在并发标记阶段因为用户程序的并发执行导致变动的数据,需暂停用户线程

筛选回收(Live Data Counting and Evacuation) 对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定回收计划

ZGC

The Z Garbage Collector (ZGC)是一种可扩展的低延迟垃圾收集器。是自从JDk11才有的,不管是物理上还是逻辑上,ZGC中已经不存在新老年代的概念了。它的数据结构会分为一个个page,当进行GC操作时会对page进行压缩,因此没有碎片问题。只能在64位的linux上使用,目前用得还是比较少的

注意:开启命令,XX:+UnlockExperimentalVMOptions -XX:+UseZGC。

比较详细地学习了各类垃圾收集器之后,需要进行归类总结一下知识点,按照垃圾收集器的作用范围,作用于“新生代”还是“老年代”可以分为如图所示:

引用官网webfolder PDF的图片:

只作用于“新生代”的有Serial、ParNew、ParalleScavenge;只作用于“老年代”的有Serial Old、CMS、Parallel Old;同时可以作用于“新生代”和“老年代”的有G1

按照是否支持并发的,可以分为串行收集器和并行收集器

串行收集器

Serial和Serial Old,只有一个垃圾回收线程执行,执行期间用户线程暂停,适用于内存比较小的嵌入式设备

并行收集器[吞吐量优先]

Parallel Scanvenge、Parallel old,多个线程执行,此时用户线程仍然处于等待状态。

并行收集器[停顿时间优先]

CMS、G1,用户线程和垃圾收集线程同时执行,但并不一定是并行的,可能是交替执行的,垃圾收集线程在执行的时候不会停顿用户线程的运行

Java JVM 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:深入小程序系列之二、Flutter 和小程序混编
下一篇:四十五、Gtihub+Hexo+icarus搭建自己的博客
相关文章