JVM 的垃圾收集算法主要包括 4 种:标记-清除算法,标记-整理算法,复制算法,分代收集算法,相比而言,分代收集算法是最常用的,也相对复杂一点,所以在此整理记录一下,加深记忆。
垃圾收集的是哪里?
首先抛出一个问题,垃圾收集,收集的到底是哪里?
我们来看一下 Java 的内存区(又叫做运行时数据区),如下图所示:
可以看出,按照线程共享和线程私有可以分为两大块,各自的组成如下:
线程共享:
堆,方法区(HotSpot 的永久代);
线程私有:
程序计数器,Java 虚拟机栈,本地方法栈;
在内存结构中,论大小,堆可以称得上是 JVM 中所管理的最大的一块内存,所以它就是垃圾收集器管理的主要目标了。
为什么要分代?
一般来说,我们将堆分为新生代和老年代,这样就可以根据各个年代的特点选择合适的垃圾收集算法。
在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成垃圾收集的过程。
而老年代的对象存活率是比较高的,且没有额外的空间对它进行分配,所以我们必须选择 “标记-清除” 或 “标记-整理” 算法进行垃圾收集。
回收是如何进行的?
从下图中可以看出,堆被分为新生代和老年代,它们之间的空间大小比例为 8:1:1,其中的新生代又被划分为 3 个部分,分别为 Eden 区,From Survivor 区和 To Survivor 区。
回收过程如下:
- 当新创建一个对象后,它会被默认分配到新生代的 Eden 区域;
- 当 Eden 区满了之后,会把 Eden 和 From Survivor 区域的存活对象统一移动到 To Survivor 区,并且给移动对象的年龄 + 1;
- 之后会回收死掉的对象,并把原来的 From Survivor 区改成 To Survivor 区,To Survivor 区改为 From Survivor 区;
- 当