什么是分代收集理论
目前大部分的JVM,在针对对象进行垃圾收集时,会将对象熬过垃圾收集的次数,视为对象的年龄。依此将对象至少划分为新生代和老年代这两个代。
分代收集理论的理论基础
分代收集理论基于以下三种假说和经验法则。
弱分代假说
绝大多数对象,在第一次垃圾收集时就会被回收,按照经验法则,这个值高达百分之九十八。
强分代假说
熬过越多次收集过程的对象越难以消亡。
跨代引用假说
该假说认为只会存在很少的跨代引用。因为只要经过一些次数的垃圾收集,即使还存在跨代引用,新生代会变成老年代,跨代引用也就自然消失了,所以跨代引用的数量不会多。在对新生代对象进行收集时,由于可能存在老年代对象引用了该对象,那么,需要找到这些老年代对象。根据跨代引用假说,这些跨代引用的数量不会太多,相比于对老年代进行扫描,在新生代建立一个全局数据结构,记录哪一块老年代内存会存在跨代引用,虽然维护这个数据结构,也需要少量的开销。但仍然显得更加合算。
根据分代理论,新生代的对象很大概率一次垃圾收集就会被回收,而老年代中的对象在下一次垃圾收集被回收的概率较小,正是由于这个区别,新生代和老年代的垃圾收集会使用不同的垃圾收集算法和垃圾收集器。
针对各分代收集的名称
Java堆分为新生代和老年代,针对收集对象处于哪一代,一共有以下四种收集方式。
- 部分收集
- 新生代收集 Minor GC/Young GC,只收集新生代垃圾对象
- 老年代收集 (Major GC/Old GC),只收集老年代垃圾对象,目前只有CMS收集器会单独收集老年代对象。需要注意的是,Major GC,目前这个说法有点混淆,有时候专指老年代收集,有时候指的是整堆收集(Full GC)。
- 混合收集(Mixed GC),收集来自整个新生代以及部分老年代中的垃圾对象。目前只有G1会有这种行为。
- 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。
三种垃圾收集算法
有三种基于标记的算法,分别是标记-清除、标记-整理和标记-复制算法。本质上,三种算法都是标记-清除两个阶段,在标记阶段,垃圾收集器将存活的对象进行标记,在回收时只保留这些对象,而将其他对象清除。只是标记-整理和标记-复制算法在清除阶段做了一些改进。三种标记算法本身与分代理论应该没有必然关系,但新生代和老年代各自的特点是选择相应算法的重要指标。
标记清除算法
是最基础最直接的算法,标记阶段标记出存活的对象,在回收阶段,将其他对象进行清除。由于要清除的对象在内存空间上不连续,需要多次清除内存,并且,清除内存的时间随回收对象的个数增加,所以执行效率低,此外,回收后内存空间不连续。但是和标记复制算法和标记整理算法相比,不需要花费额外的时间进行复制或整理。并且和标记复制算法相比,不需要额外的空间放置复制的对象。
标记复制算法
将标记为存活的对象复制到一块连续的内存区域,将剩下的内存一次性清除干净。优点是清除后内存空间连续。缺点是需要预留一部分空间用于存放不被清除的对象,降低了内存的使用率。同时,复制对象也有时间上的开销。
标记整理算法
将标记为存活的对象移动到一端,将剩下的内存一次性清除干净。优点是清除后内存空间连续。缺点是移动对象在时间上有开销。
优点 | 缺点 | |
---|---|---|
标记清除 | 不需要移动数据 | 需要多次耗时清除内存、回收后内存空间不连续 |
标记整理 | 一次清除一整块内存、回收后内存空间连续 | 整理时需移动数据,停顿时间长 |
标记复制 | 不需要移动数据、停顿时间短、一次清除一整块内存、回收后内存空间连续 | 需要划出一块内存,复制数据也还是有一点开销 |
标记复制算法一般会比其它两种算法快十倍左右。当继续存活的对象比较少时,应当使用标记复制算法。在新生代中,根据经验值,98%的对象在第一次GC时就会被回收,所以适合使用标记复制算法。老年代中,对象已经存活了很长时间,这一轮GC中被回收的概率较低,此时,为了避免不断将对象进行复制的开销,应当使用标记清除或标记整理算法。