Bootstrap

JVM垃圾回收机制

JVM垃圾回收机制

一、什么是垃圾回收机制

垃圾回收是一种自动的存储管理机制。当一些被占用的内存不再需要时,就应该予以释放,以让出空间,这种存储资源管理,称为垃圾回收。

JVM内存

JVM将内存划分为五个区间。

JVM分为五大内存空间,程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回收都具备确定性,就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而Java堆区和方法区则不一样,这部分内存的分配和回收是动态的,正是垃圾收集器所需关注的部分。

年轻代分为Eden区和survivor区(两块儿:from和to),且Eden:from:to==8:1:1。

jvm内存结构

1)新产生的对象优先分配在Eden区(除非配置了-XX:PretenureSizeThreshold,大于该值的对象会直接进入年老代);

2)当Eden区满了或放不下了,这时候其中存活的对象会复制到from区。

这里,需要注意的是,如果存活下来的对象from区都放不下,则这些存活下来的对象全部进入年老代。之后Eden区的内存全部回收掉。

3)之后产生的对象继续分配在Eden区,当Eden区又满了或放不下了,这时候将会把Eden区和from区存活下来的对象复制到to区(同理,如果存活下来的对象to区都放不下,则这些存活下来的对象全部进入年老代),之后回收掉Eden区和from区的所有内存。

4)如上这样,会有很多对象会被复制很多次(每复制一次,对象的年龄就+1),默认情况下,当对象被复制了15次(这个次数可以通过:-XX:MaxTenuringThreshold来配置),就会进入年老代了。

5)当年老代满了或者存放不下将要进入年老代的存活对象的时候,就会发生一次Full GC(这个是我们最需要减少的,因为耗时很严重)。

二、堆区的垃圾回收

什么是垃圾

如果应该对象已经没有任何应该地方引用他,他就是垃圾

怎么确认是垃圾

引用计数法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一。反之每当一个引用失效时,计数器减一。当计数器为0时,则表示对象不被引用。

可达分析

设立若干根对象(GC Root),每个对象都是一个子节点,当一个对象找不到根时,就认为该对象不可达。

触发条件

Minor GC触发机制

当年轻代满时就会触发Minor GC,这里的年轻代指的是Eden代满,Survivor满不会引发GC。

FULL GC触发机制

老年代空间不足方法区空间不足通过Minor GC后进入老年代的平均大小大于老年代的可用内存。由Eden区、From Survivor区向To Survivor区复制时,对象大小大于To Space可用内存,则把对象转存到老年代,且老年代的可用内存小于该对象大小System.gc()方法的调用,系统建议执行Full GC,但是不必然执行。

三、垃圾回收算法

标记清除

标记清楚算法将垃圾回收分为俩个阶段:标记阶段和清除阶段。

在标记阶段首先通过根节点(GC Roots),标记所有从根节点开始的对象,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。

  • 适用场合:
    • 存活对象较多的情况下比较高效
    • 适用于年老代(即旧生代)
  • 缺点:
    • 容易产生内存碎片,再来一个比较大的对象时(典型情况:该对象的大小大于空闲表中的每一块大小但是小于其中俩块的和),会提前触发垃圾回收机制
    • 扫描了整个空间俩次(第一次:标记存活对象;第二次:清除没有标记的对象)

复制算法

从根集合节点进行扫描,标记出所有的存活对象,并将这些存活的对象复制到一块新的内存,之后将原来的那一块儿内存全部回收掉;

现在的商业虚拟机都采用这种收集算法来回收新生代。

适用场合:

  • 存活对象较少的情况下比较高效
  • 扫描了整个空间一次(标记存活对象并复制移动)
  • 适用于年轻代(即新生代):基本上98%的对象是“朝生夕死”的,存活下来的会很少

缺点:

  • 需要一块儿空的内存空间
  • 需要复制移动

标记整理

复制算法的高效性是建立在存活对象少、垃圾对象多的前提下。

这种情况在新时代经常发生,但是在老年代更常见的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活的对象较多,复制的成本也将很高。

标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。

首先也需要从根节点开始对所有可达对象做一次标记,但之后,它并不是简单的清除未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。这种方法既避免了碎片的产生,又不需要俩块相同的内存空间,因此,其性价比比较高。

分代收集算法

分代收集算法就是目前虚拟机使用的回收算法,它解决了标记整理不适用于老年代的问题,将内存分为各个年代。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),在堆区之外还有一个代就是永久代(Permanet Generation)。

在不同年代使用不同的算法,从而使用最适合的算法,新生代存活率低可以使用复制算法。而老年代对象存活率高,没有额外空间对它进行分配担保,所以只能使用标记清除或者标记整理算法。

;