0
点赞
收藏
分享

微信扫一扫

Java基础-JVM内存管理-垃圾回收算法

高子歌 2021-09-30 阅读 74

Java工程师知识树 / Java基础


垃圾收集算法

标记清除算法

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

它的主要不足有两个:一是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片。

标记-清除算法的执行过程见下图:


复制算法

为了解决效率问题,“复制”算法出现了。它将内存空间划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活的对象复制到另外一块上,然后再把已使用的的内存空间一次性清理掉。

这样每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。但是这种算法的代码是将内存空间缩小为原来的一半。

复制算法的执行过程见下图:

标记-整理算法

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

标记-整理算法的执行过程见下图:

分代收集算法

当前商业虚拟机的垃圾收集都采用“分代收集”的算法,这种算法只是根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

总结:
在新生代中,每次垃圾收集都发现有大批对象死去,只有少量存活,那就选用“复制算法“,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高,没有额外空间对它进行分配担保,就必须使用”标记-清理“算法或者”标记-整理“算法。

垃圾收集器

堆内存是垃圾收集器主要回收垃圾对象的地方,堆内存可以根据对象生命周期的不同分为新生代和老年代,分代收集,新生代使用复制算法,老年代使用标记清除或者标记整理算法。

HotSpot虚拟机提供了7中垃圾收集器,其中新生代三种:Serial/ParNew/Parallel Scavenge收集器,老年代三种:Serial Old/Parallel Old/CMS,都适用的是G1收集器。所有垃圾收集器组合 情况如下图:

Serial收集器

最基本也是发展历史最长的垃圾收集器,在进行垃圾收集时,必须Stop The World(暂停其他工作线程),直到收集结束。只使用一条线程完成垃圾收集,但是效率高,因为没有线程交互的开销,拥有更高的单线程收集效率。发生在新生代区域,使用复制算法。

ParNew收集器

Serial收集器的多线程版本。在进行垃圾收集时同样需要Stop The World(暂停其他工作线程),直到收集结束。使用多条线程进行垃圾收集(由于存在线程交互的开销,所以在单CPU的环境下,性能差于Serial收集器)。目前,只有Parnew收集器能与CMS收集器配合工作。发生在新生代区域,使用复制算法。

Parallel Scavenge收集器

ParNew收集器的升级版,具备ParNew收集器并发多线程收集的特点,以达到可控制吞吐量为目标。(吞吐量:CPU用于运行用户代码的时间与CPU总消耗时间(运行用户代码时间+垃圾收集时间)的比值)。该垃圾收集器能根据当前系统运行情况,动态调整自身参数,从而达到最大吞吐量的目标。(该特性成为GC自适应的调节策略)。发生在新生代,使用复制算法。

Serial Old收集器

Serial 收集器应用在老年代的版本。并发、单线程、效率高。使用标记整理算法。

Parallel Old收集器

是Parallel Scavenge应用在老年代的版本,以达到可控制吞吐量、自适应调节和多线程收集为目标,使用标记整理算法。

CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。使用这类收集器的应用重视服务的响应速度,希望系统停顿时间最短,以带来更好的用户体验。 使用标记清除算法,一共四个步骤:初始标记、并发标记、重新标记和并发清除。详情见下表:

下面说下CMS的优缺点:

优点:(1)并行:用户线程和垃圾收集线程同时进行。

(2)单线程收集:只使用一条线程完成垃圾收集。

(3)垃圾收集停顿时间短:获取最短的回收停顿时间,即希望系统停顿的时间最短,提高响应速度。

缺点:

(1)总吞吐量会降低:因为该收集器对CPU资源非常敏感,在并发阶段不会导致停顿用户线程,但会因为占用部分线程(CPU资源)导致应用程序变慢,总吞吐量会降低。

(2)无法处理浮动垃圾:由于并发清理时用户线程还在运行,所以会有新的垃圾不断产生,只能等到下一次GC时再清理。(因为这一部分垃圾出现在标记过程之后,所以CMS 无法在当次GC中处理他们,因此CMS无法等到老年代填满再进行Full GC,CMS需要预留一部分空间)。

(3)垃圾收集后会产生大量的内存碎片:因为CMS收集器是使用标记-清除算法的。

下面一张图了解下CMS的工作过程:

G1收集器

G1收集器是最新最前沿的垃圾收集器。特点如下:

(1)并行:用户线程和垃圾收集线程同时进行。 (2)多线程:即使用多条垃圾收集线程进行垃圾回收。(并发和并行充分利用多CPU和多核环境的硬件优势来缩短垃圾收集的停顿时间) (3)垃圾收集效率高:G1收集器是针对性对Java堆内存区域进行垃圾收集,而非每次都对整个区域进行收集。即G1除了将Java堆内存分为新生代和老年代之外,还会细分为许多个 大小相等的独立区域(Region),然后G1收集器会跟踪每个Region里的垃圾代价值大小,并在后台维护一个列表。每次回收时,会根据允许的垃圾收集时间优先回收价值最大的 Region,从而避免了对整个Java堆内存区域的回收,提高了效率。因为上述机制,G1收集器还能建立可预测的时间模型:即让使用者明确执行一个长度为M毫秒的时间片段,消耗在 垃圾收集上的时间不得超出N毫秒。即具备实时性。 (4)不会产生内存碎片。从整理上看,G1收集器是基于标记-整理算法的,从局部看是基于复制算法的。在新生代使用复制算法,在老年代使用标记-整理算法。

下面了解下工作流程,跟CMS有点像。

下面是G1的工作过程:

举报

相关推荐

0 条评论