目录
  • 前言
  • 什么是分代回收?
  • 为什么采用分代回收?
    • 年轻代回收
    • 老年代回收
    • 更加厉害的回收方式
  • 垃圾收集器的权衡

    前言

    我们今天一起来聊一聊关于垃圾收集的细节问题。垃圾收集是通过何种方式减少stop the world?这将是垃圾回收的重点内容。

    什么是分代回收?

    什么是分代回收,初次接触的同学肯定是很懵的。还记得我们前面在介绍使用jvisualvm工具的时候,从它给我们反馈的视图上看到,有几个不同的数据块,且是动态的,如下图红圈的部分:

    java性能优化之分代回收

    从左到右分别是:

    • Metaspace :元空间
    • Old :老年代
    • Eden + S0 + S1 : 这三个加在一起称为年轻代

    到目前为止,市面上常见的垃圾收集器,都是按照这种分代回收的方式进行垃圾回收的,但是其内部的实现方式却有很大的差别。

    本章我们的重点是 老年代 和 年轻代 。

    通过上图我们发现,年轻代有三部分组成:

    • Eden :通常称为Eden区,翻译过来也可以称为伊甸园区。
    • S0 和 S1:通常称为survivor区,翻译过来称为幸存者区,由幸存者0和幸存者1所组成。

    为什么采用分代回收?

    绝大部分的对象,它们的生命周期非常短暂,甚至绝大多数都是临时对象,垃圾收集器的设计需要适应这种情况。

    我们都知道对象创建,存放在堆中,而不论是老年代还是年轻代都是堆中的一部分。

    年轻代回收

    当对象被创建后,会被分配至年轻代,随着对象的增加,年轻代会被占满,此时将会停止全部的应用线程,并进行垃圾回收,没有被使用的对象会被回收,仍然被使用的对象将会被移动到其他的地方。这种操作就是MinorGC,年轻代回收。

    使用分代算法的最根本原因,是为了尽量的减少垃圾回收造成的停顿,我们可以从下面两个方面考虑:

    • 新生代是堆的一部分,仅处理一部分空间,一定比整个堆空间的时间要短,停顿时间也就短。但是我们一定会想到,停顿的频率增加了。
    • 年轻代的空间分配方式,对性能有影响。年轻代中,eden占据大部分空间,而S0和S1平分剩余空间。对象首次创建会在Eden中,经过一次垃圾回收时,Eden被清空,未使用的对象被回收,仍然使用的对象进入Survior或老年代。Eden区的清空操作,相当于进行了一次压缩整理

    即使是年轻代的回收,仍然存在时空停顿

    老年代回收

    前面提到,除了在Eden可能将对象移动到老年代当中,对于在Survior当中没有被回收的对象,最终也会移动到老年代当中。当老年代被占满时,会停止所有的应用线程,找到不再使用的对象进行垃圾回收。这个过程将会停顿很长时间,我们称之为Full GC

    更加厉害的回收方式

    前面的描述其实都是较为简单的垃圾收集器,在停止应用线程后去发现不再使用的对象,进行回收。

    然而实际上,通过一些定制,和复杂的计算,我们可以在应用线程运行时去找到不再使用的对象。在前一篇文章一笔带过的CMS和G1收集器,就是如此。他们不需要停止应用线程就可以找到不再使用的对象,所以它们也叫做concurrent垃圾收集器

    垃圾在查找的时候将会占用很多时间,当然查找算法将是下一章节我们需要学习的内容。然而concurrent垃圾收集器可以尽可能的减少应用的停顿时间,它们也可以称为低停顿收集器

    这种垃圾收集器缩减应用的停顿时间,其代价是占用更多的CPU。即使是G1和CMS也会遇到长时间的Full GC,这将是我们需要针对实际环境调优的方向。

    垃圾收集器的权衡

    前面简单描述了不同垃圾收集器的垃圾收集方式,以及造成的影响。但是在我们选择垃圾收集器的过程中还是需要一定的权衡,才能使其发挥最佳的性能。

    我们需要考虑的重点就是:

    • 吞吐量:如果你的系统需要大批量的处理数据等,换句话说,不要求每次响应时间最快,而平均响应时间更加重要,Parallel收集器也许会有不错的表现。
    • 响应时间:简单来说,如果你想要你的接口获得更快的响应时间,那么concurrent收集器将是更好的选择。
    • CPU性能:使用current收集器势必要消耗更多的CPU资源。
    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。