JVM垃圾回收机制总结

对于垃圾回收机制我先抛出三个问题:

①哪些内存需要回收?

②什么时候回收?

③如何回收?

下面我们主要针对这三个问题来研究JVM GC

一、哪些内存需要回收?

1.JAVA使用可达性分析法来判断对象是否需要回收。

这个算法的基本思路是通过一系列称为“GC ROOTS”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC ROOTS没有任何引用链的话,则此对象不可用。可回收。

GC ROOTS对象包括以下几种:

①虚拟机栈(栈帧中的本地变量表)中引用的对象

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

③方法区中常量引用的对象

④本地方法栈JNI引用的对象

2.对象在作可达性分析后如果没有与任何GC ROOTS关联那么将会被标记筛选,如果它覆盖了finalize()方法则它将会被放置在一个F-Queue队列中,并由一个Finalizer线程去执行,如果在finalize方法中成功拯救了自己(将this引用赋值给每个类变量或者成员变量),则可避免被回收。但是强烈不建议使用对象的finalize()方法。在这里我只是把我知道的记录一下。

二、什么时候回收?

1.安全点

程序在执行时并不是在所有地方都能停下来进行GC,只有到达安全点才能暂停。对于Safepoint,需要考虑的问题是如何在GC发生时让所有线程都跑到最近的安全点再停顿下来。这里有两种方案可选:

①抢占式中断

抢占式中断不需要线程的执行代码主动去配合,在GC发生时,首先把所有线程全部中断,如果发现线程中断的地方不在安全点上,就恢复线程,让它跑到安全点上。

②主动式中断

主动式中断不直接对线程操作,仅仅设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就自己中断挂起,轮询标志和安全点是重合的,另外加上创建对象需要分配内存的地方。

2.安全区域

安全区域是指在一段代码中,引用关系不会发生变化,在这个区域的任何地方开始GC都是安全的。

线程执行到safe region中的代码时,首先标识自己进入safe region,那在这段时间里JVM要发起GC时就不用管那些已经标识自己为safe region状态的线程了。当线程要离开safe region时,检查系统是否已经完成了根节点枚举,如果完成了,那线程就继续执行否则就等待直到可以离开safe region的信号为止。

三、如何回收?

1.回收算法:

1)标记-清除算法

缺点:①效率问题,标记和清除两个过程的效率都不高;②空间问题,清除后会产生大量不连续的内存碎片,导致大对象无法分配而提前出发GC

2)复制算法

复制算法是将内存分为大小相等的两块,每次只是使用其中的一块。当一块内存使用完之后,将还存活的对象复制到另一块内存中,然后清空内存。

缺点:内存容量只使用了一半。

现在的商业虚拟机都采用这种方式来回收新生代。使用一块eden和两块survivor区,eden:survivor=8:1,每次只使用eden和一块survivor,然后将存活对象复制到另一块survivor上。

注意,这里我们不能保证每次回收都只有不多于10%的对象存活,当survivor空间不够时需要依赖其他内存(老年代)进行分配担保。

3)标记-整理算法

标记过程与标记-清除算法相同,后续不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存

4)分代收集算法

针对不同的内存区域采用不同的回收算法,比如年轻代采用复制算法,老年代采用标记-清除或者标记-整理算法。

四、垃圾回收器

1.serial收集器

单线程收集器,年轻代采用复制收集算法,老年代采用标记-整理算法

2.ParNew收集器

是Serial收集器的多线程版本

3.Parallel Scavenge收集器

采用复制算法的多线程新生代收集器,与ParNew不同的是,其目的是达到一个可控制的吞吐量。(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))

4.Parallel Old收集器

Parallel Scavenge的老年代版本。

5.CMS收集器

以获取最短回收时间为目标的收集器,基于标记-清除算法

6.G1收集器

特点:

①并行与并发

②分代收集

G1不需要与其他收集器配合,管理整个GC堆

③空间整合

从整体上来看属于标记-整理算法,从局部看属于复制算法,不会产生内存碎片

④可预测停顿

原文地址:https://www.cnblogs.com/CLAYJJ/p/8253848.html