JoJo的个人博客

记录精彩的程序人生

目录
GC机制
/  

GC机制

概述

每个程序员都遇到过内存溢出的情况,程序运行时,内存空间是有限的,那么如何及时的把不再使用的对象清除将内存释放出来,这就是GC要做的事。

1. 需要 GC 的内存区域

内存垃圾回收主要集中于java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。

2. GC 的对象

需要进行回收的对象就是已经没有存活的对象,判断一个对象是否存活常用的有两种办法:引用计数和可达分析。

(1)引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。

(2)可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。

3. 触发 GC 的时机

(1)程序调用System.gc时可以触发

(2)系统自身来决定GC触发的时机(根据Eden区和From Space区的内存大小来决定。当内存大小不足时,则会启动 GC 线程并停止应用线程)

GC 又分为 minor GC 和 Full GC (也称为 Major GC )

Minor GC 触发条件:

当 Eden 区满时,触发 Minor GC

Full GC 触发条件:

a. 调用 System.gc 时,系统建议执行 Full GC,但是不必然执行

b. 老年代空间不足

c. 方法区空间不足

d. 通过 Minor GC 后进入老年代的平均大小大于老年代的可用内存

e. 由 Eden 区、From Space 区向 To Space 区复制时,对象大小大于 To Space 可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

GC 常用算法

1. 标记-清除算法

最基础的收集算法,算法为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

优点 最大的优点是,标记—清除算法中每个活着的对象的引用只需要找到一个即可,找到一个就可以判断它为活的。此外,更重要的是,这个算法并不移动对象的位置。

缺点 它的缺点就是效率比较低(递归与全堆对象遍历)。每个活着的对象都要在标记阶段遍历一遍;所有对象都要在清除阶段扫描一遍,因此算法复杂度较高。没有移动对象,导致可能出现很多碎片空间无法利用的情况。

2. 标记-整理算法

老年代的 GC,标记-压缩法是标记-清除法的一个改进版。同样,在标记阶段,该算法也将所有对象标记为存活和死亡两种状态;不同的是,在第二个阶段,该算法并没有直接对死亡的对象进行清理,而是将所有存活的对象整理一下,放到另一处空间,然后把剩下的所有对象全部清除。这样就达到了标记-整理的目的。

优点

该算法不会像标记-清除算法那样产生大量的碎片空间。

缺点

如果存活的对象过多,整理阶段将会执行较多复制操作,导致算法效率降低。

3. 复制算法

新生代的 GC,该算法将内存平均分成两部分,然后每次只使用其中的一部分,当这部分内存满的时候,将内存中所有存活的对象复制到另一个内存中,然后将之前的内存清空,只使用这部分内存,循环下去。

优点

实现简单;不产生内存碎片。

缺点 每次运行,总有一半内存是空的,导致可使用的内存空间只有原来的一半。

4. 分代回收算法

现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代(Young)和老年代(Tenure)。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记-整理 或者 标记-清除。

具体过程:新生代(Young)分为 Eden 区、From 区与 To 区

当系统创建一个对象的时候,总是在Eden区操作,当这个区满了,那么就会触发一次YoungGC,也就是年轻代的垃圾回收。一般来说这时候不是所有的对象都没用了,所以就会把还能用的对象复制到From区。

这样整个Eden区就被清理干净了,可以继续创建新的对象,当Eden区再次被用完,就再触发一次YoungGC,然后呢,注意,这个时候跟刚才稍稍有点区别。这次触发YoungGC后,会将Eden区与From区还在被使用的对象复制到To区。

再下一次YoungGC的时候,则是将Eden区与To区中的还在被使用的对象复制到From区。

经过若干次YoungGC后,有些对象在FromTo之间来回游荡,这时候From区与To区亮出了底线(阈值),这些对象要是到现在还没回收,就一起(复制)到老年代。

老年代经过这么几次折腾,也就扛不住了(空间被用完),那就来次集体大扫除FullGC,也就是全量回收。

垃圾收集器

Serial 收集器

Serial/Serial Old收集器是最基本、发展历史最悠久的收集器,属于单线程的收集器,采用复制算法进行垃圾收集,它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。

ParNew 收集器

并行,ParNew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余行为与Serial收集器完全一样。

Parallel Scavenge 收集器

并行,Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器。 其特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,其目标则是达到一个可控制的吞吐量(Throughput)

Serial Old 收集器

串行,其是Serial收集器的老年代版本,同样是单线程收集器,使用“标记-整理”算法。

Parallel Old 收集器

并行,其是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。

CMS 收集器

Sun也称其为Concurrent Low Pause Collector(并发低停顿收集器)其是一种以获取最短回收停顿时间为目标的收集器。其是基于“标记-清除”算法实现。

G1 收集器

是目前最刁的收集器技术之一,G1是一款面向服务端应用的垃圾收集器。它的使命是在未来可以替换掉JDK1.5中发布的CMS收集器。

参考文章

Java GC 机制详解

Java 虚拟机详解 04----GC 算法和种类【重要】

JVM——垃圾收集器


标题:GC机制
作者:SunnySky
地址:https://www.tianyang.pub/articles/2020/06/05/1591350348015.html

评论