Java垃圾回收算法

Java垃圾回收算法


在上一篇中,我讲述了Java虚拟机如何判断一个对象是否需要回收,这一篇就来讲讲当判定需要回收的时候,虚拟机是如何回收”垃圾“的;

1.标记——清除法

根据名称就可以想到,该回收算法是将需要回收的对象进行标记,然后在标记完所有该回收的对象后进行统一回收;

下面来看这张图,可一目了然:

Java垃圾回收算法

当进行回收之后,可以明显发现这种方法的不足:

  • 标记清除后会产生大量不连续的内存碎片 ,空间碎片太多可能会导致以后在程序运行中需要分配较大
    对象时,无法找到足够连续内存而不得不提前触发另一次垃圾收集;
  • 还有一个问题就是,其实标记和清楚这两个过程的效率都比较低;

2. 复制算法(新生代回收算法(Minor GC))

顾名思义:这种算法我们采用一分为二的思想,每次将内存一分为二,每次只使用一半,当发生垃圾回收时,我们将这一半中的存活对象依次复制到另一半中,然会对这一半内存进行一次性清除;
这样一来,是不是解决了标记清楚法的两大不足????;

呈上一图,一目了然:
Java垃圾回收算法

Java垃圾回收算法

以这种算法为思想,稍作一点改进,就是我们的新生代回收算法;
具体改进:

  • 内存不按一比一进行划分,而是将内存(新生代内存)划分为一块较大的Eden(伊甸园)空间,和两块较小的survivor(幸存者)空间;
  • 两块survivor空间,一块叫From区,一块叫To区;
  • Eden区与survivor区的比例为8:1;
  • Eden区与From区用来使用,此时可使用空间提升到了90%;

回收具体步骤:

  1. 当Eden区满的时候,会触发第一次Minor gc,把还活着的对象拷贝到Survivor From区;当Eden区再次触发
    Minor gc的时候,会扫描Eden区和From区域,对两个区域进行垃圾回收,经过这次回收后还存活的对象,则直
    接复制到To区域,并将Eden和From区域清空。
  2. 当后续Eden又发生Minor gc的时候,会对Eden和To区域进行垃圾回收,存活的对象复制到From区域,并将
    Eden和To区域清空。
  3. 部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个
    参数默认是15),最终如果还是存活,就存入到老年代

为啥这是新生代回收算法

这种算法在新生代中被商用虚拟机大量采用,包括Hotspot虚拟机;

到这里很多人疑问,这样一来,只有10%的内存留给我们的存活对象,这难道不会不够吗?
答案:之所以叫新生代,这里的对象大都具备朝生夕死的特点,所以存活下来的很少;

3.标记——整理算法(老年代回收算法(Full GC))

在老年带中,这里的大多数对象都不会被回收,采用复制算法的话会带来大量的复制操作,所以采用上面的算法是显然不可行的;

这里我们采用标记整理算法,这种算法就是对标记清楚法进行了一个改进:

  • 在进行回收的时候,不是进行标记完后进行统一回收,而是让所有存活对象向一端移动,然后直接清理掉剩余的空间;

呈上一图:

Java垃圾回收算法

4.分代收集算法(Java虚拟机所采用)

分代收集算法其实就是结合了复制算法和标记整理算法;

当前JVM垃圾收集都是采用的这种算法 ,即根据对象的存活时间不同,将内存划分为几个区域;
一般是把Java堆分为新生代和老年代 。在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没有额外空间对它进行分配担保,就必须采用"标记-清理"或者"标记-理"算法;

经典面试题: Minor GC和Full GC这两种GC有什么不一样吗?

  1. Minor GC又称为新生代GC : 指的是发生在新生代的垃圾收集。因为Java对象大多都具备朝生夕灭的特性,因此Minor GC(采用复制算法)非常频繁,一般回收速度也比较快
  2. Full GC 又称为 老年代GC或者Major GC : 指发生在老年代的垃圾收集。出现了Major GC,经常会伴随至少一次的Minor GC(并非绝对,在Parallel Scavenge收集器中就有直接进行Full GC的策略选择过程)。
    Major GC的速度一般会比Minor GC慢10倍以上