JVM-02

1.1 GC

1.1.1 对象存活判断

  • 引用计数法:
    经典的引用计数算法,每个对象添加到引用计数器,每被引用一次,计数器+1,失去引用,计数器-1,当计数器在一段时间内为0时,即认为该对象可以被回收了。但是这个算法有个明显的缺陷:当两个对象相互引用,但是二者都已经没有作用时,理应把它们都回收,但是由于它们相互引用,不符合垃圾回收的条件,所以就导致无法处理掉这一块内存区域。

  • 根搜索法:

    基本思想是:从一个叫GC Roots的根节点出发,向下搜索,如果一个对象不能达到GC Roots的时候,说明该对象不再被引用,可以被回收。如上图中的Object5、Object6、Object7,虽然它们三个依然相互引用,但是它们其实已经没有作用了,这样就解决了引用计数算法的缺陷。
    那么到底哪些对象可以作为GC root呢?

  • 虚拟机栈中应用的对象

  • 方法区里面的静态对象

  • 方法区常量池的对象

  • 本地方法栈JNI应用的对象

1.1.2 垃圾回收算法

  • 标记-清除算法(Mark-Sweep)

标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。

这种算法带来的最大问题就是,产生了大量的内存碎片,如果一个新的对象需要一片很大的连续的内存空间就无法得到满足了。
优点:简单
缺点:容易产生大量碎片

  • 标记-压缩
    该算法与标记-清除算法类似,都是先对存活的对象进行标记,但是在清除后会把活的对象向左端空闲空间移动,然后再更新其引用对象的指针,如下图所示:

但由于需要进行移动,因此成本也增加了。(该算法适用于旧生代)

  • 复制算法
    它将可用内存划分为两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
    Java的新生代串行垃圾回收器中使用了复制算法的思想。新生代分为 eden 空间、survivor from 空间、survivor to 空间 3 个部分。其中survivor from 空间和 survivor to 空间可以视为用于复制的两块大小相同、地位相等,且可进行角色互换的空间块。默认Eden与Survivor的比例是8:1

这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。

很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。

优点:简单,不易产生碎片

缺点:可用内存变少,且如果存活对象较多,则复制的效率会大大降低。

  • 分代回收算法(Generational Collection)
    分代回收算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活周期的不同将内存划分为几块。根据每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率。

以HotSpot虚拟机为例,它将所有的新建对象都放入称为年轻代的内存区域,年轻代的特点是对象会很快回收,因此,在年轻代就选择效率较高的复制算法。当一个对象经过几 次回收后依然存活,对象就会被放入称为老生代的内存空间。在老生代中,几乎所有的对象都是经过几次垃圾回收后依然得以幸存的。因此,可以认为这些对象在一 段时期内,甚至在应用程序的整个生命周期中,将是常驻内存的。如果依然使用复制算法回收老生代,将需要复制大量对象。再加上老生代的回收性价比也要低于新生代,因此这种做法也是不可取的。根据分代的思想,可以对老年代的回收使用与新生代不同的标记-压缩算法,以提高垃圾回收效率。

目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间(一般为8:1:1),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。

而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。

年轻代一般使用复制算法,年老代一般使用标记整理算法

2.1 垃圾回收器

新生代收集器:Serial、ParNew、Parallel Scavenge

老年代收集器:Serial Old、Parallel Old、CMS

  • Serial 收集器

串行GC,采用复制算法

  • ParNew 收集器

并行GC,采用复制算法,相当于Serial的多线程版本

  • Parallel Scavenge 收集器

并行GC,采用复制算法。追求高吞吐量,高效利用CPU。吞吐量一般为99%, 吞吐量= 用户线程时间 / (用户线程时间+GC线程时间)

这里注意,经过测试JDK 1.8.0_152默认垃圾收集器为:PS MarkSweep 和 PS Scavenge,而并不是G1

  • Serial Old 收集器

串行GC,采用标记整理算法,Serial老年代版本

  • Parallel Old 收集器

并行GC,采用标记整理算法,它是Parallel Scavenge收集器的老年代版本

  • CMS 收集器

并行GC,并发,采用标记清除算法

CMS(Concurrent Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器。

CMS整个过程分为4个步骤:

初始标记(CMS initial mark) -stop the world
并发标记(CMS concurrent mark)
重新标记(CMS remark) -stop the world
并发清除(CMS concurrent sweep)

  • G1 收集器

最先进的收集器,独立完成分代回收

3.1 垃圾收集常用参数

posted @ 2020-09-18 20:08  西伯利亚爱学习的狼  阅读(123)  评论(0编辑  收藏  举报