Java内存学习笔记04-垃圾收集算法与垃圾收集器

Posted 双木青橙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java内存学习笔记04-垃圾收集算法与垃圾收集器相关的知识,希望对你有一定的参考价值。

1.垃圾收集算法

1.1 标记-清除算法

最基础的收集算法是“标记-清除”(Mark-sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象,后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。它的主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另一个是空间问题,标记清除之后会产生大量的不连续的内存碎片,空间碎片可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作

1.2 复制算法

复制(Copying)收集算法是将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。
优点:每次都是对其中的一块进行内存回收,内存分配时也不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效
缺点:内存缩小为原来的一半
现在的商业虚拟机都采用这种收集算法来回收新生代,IBM的专门研究表明,新生代中的**对象98%**是朝生夕死的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor。当回收时,将Eden和Survivor中存活着的对象一次性地拷贝到 另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor的空间
Hotspot虚拟机默认Eden和Survivor的大小比例是:8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存是会被“浪费”的。

1.3 标记-整理算法(Mark-Compact)

老年代的特点是:对象存活对象时间长,不会轻易被回收。有人据此提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存,

2.4 分代收集算法

当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,根据对象的存活周期的不同将内存划分为几块,一般把Java堆分为新生代和老年代,这样就可以根据各个年代的特点 采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本 就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-整理”,或者标记-清理算法进行回收。

3 垃圾收集器

收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现。

如上展示了7种作用于不同分代的收集器材,如果两个收集器之间存在连线,就说明它们可以搭配使用。有一点需要注意:直到现在为止还没有最好的收集器出现,更加没有万能的收集器,所以物们选择的只是对具体应用最合适的收集器

3.1 Serial收集器

Serial收集器是最基本,历史最悠久的收集器,曾经(在JDK1.3.1之前)是虚拟机新生代收集的唯一选择。属于单线程的收集器。在进行垃圾收集时,**必须暂停其他所有的工作线程(“stop the world”)**直到它收集结束。

3.2 ParNew收集器

ParNew收集器其实就是Serial 收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括与Serial 收集器完全一样。
ParNew是运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集外,目前只有它能与CMS收集器配合工作。

3.3 Parallel Scavenge收集器

Parallel Scavenge 也是新生代收集器,它也是使用复制算法并且并行的收集器。
Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS收集器的关注点尽可能地缩短垃圾收集时用户线程的停顿时间,而Parllel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是CPU用于运行用户代码时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户的体验;而高吞吐量则可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务

3.4 Serial Old收集器

Serial Old收集器是Serial 收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。这个收集器的主要意义也是被Client模式下的虚拟机使用,如果在Server模式下,它主要还有两大用途:一个是在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用,另外一个就是作为CMS收集器的后备预案,在并发收集器发在Concurrent Mode Failure的时候使用。

3.5 Parallel Old收集器

Parallel Old 是Parallel Scavenge 收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器是在JDK1.6中才开始提供的。在注重吞吐量及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。

3.6 CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。**其是基于“标记-清除”**算法实现的。
整个过程分为4个步骤

  • 初始标记(CMS initial mark)
  • 并发标记(CMS concurrent mark)
  • 重新标记(CMS remark)
  • 并发清除(CMS concurrent sweep)

    其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅标记一下 GC Roots能直接关联到的对象。速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续动作而导致标记产生变动的那一部分对象的标记记录,重新标记阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
    CMS 收集器有三个显著的缺点:
  • CMS 收集器对CPU资源非常敏感,CMS 默认启动的回收线程数是(CPU数量+3)/4。
  • CMS收集器无法处理浮动垃圾(Floating Garbage)
  • 基于“标记-清除”算法在收集结束会产生大量的空间碎片

3.7 G1收集器

G1(Garbage First),G1收集器是垃圾收集器理论进一步发展的产物,它与前面的CMS收集器相比有两个显著的改进:一是G1收集器是基于“标记-整理”算法实现的收集器,就也就是说它不会产生空间碎片,这对于长二是它可以非常精确地控制停顿,即能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
G1可以实现在基本上不牺牲吞吐量的前提下完成低停顿的内存回收,而G1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域(Region),并且跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集旱,优先回收垃圾最多的区域(这就是Garbage First名称的由来)。区域划分及有优先级的区域回收,保证了G1收集器在有限的时间内可以获得最高的收集效率。

3.8 垃圾收集器参数总结

https://blog.csdn.net/tolmanlau/article/details/107398449

学习材料

《深入理解Java虚拟机JVM高级特性与最佳实践第3版》第3章

以上是关于Java内存学习笔记04-垃圾收集算法与垃圾收集器的主要内容,如果未能解决你的问题,请参考以下文章

Java学习笔记3.11.1 垃圾回收 - 垃圾回收的作用

JVM垃圾收集算法学习笔记

《深入理解Java虚拟机》读书笔记-垃圾收集器与内存分配策略

《深入理解Java虚拟机》读后笔记-垃圾收集算法

《深入理解Java虚拟机》读后笔记-垃圾收集算法

Java垃圾收集器与内存分配策略