这是我参与更文挑战的第 23 天,活动详情查看: 更文挑战
垃圾回收清除阶段相关算法
3.标记-压缩(整理)算法
概述:
复制算法的高效性是在存活对象少、垃圾对象多的前提下,这种情况经常在新生代发生,但是老年代恰恰相反。此时再使用复制算法,回收的成本将很高。因此基于老年代的垃圾回收特性,需要使用其他算法。标记清除算法虽然可以使用,但是执行完回收后会产生内存碎片,导致大对象无法存入,所以在此基础上改进,出现了标记-压缩算法。
执行过程:
第一阶段和标记-清除算法相同,在第二阶段将所有的存活对象压缩到内存的一侧,按顺序排放,然后清理之外的空间。
标记-压缩算法就等同于标记-清除算法执行完成后再执行一次内存碎片整理,因此标记-压缩算法也可以称为标记-清除-压缩算法。
二至的本质区别在于标记-清除算法是一种非移动式的回收算法,标记-压缩算法是一种移动式的回收算法。是否移动是一个优缺点并存的决策。好处是内存碎片的整理,坏处是效率低,并且所有引用到被移动的对象的变量都要修改引用的地址值。
优点:
- 解决了标记-清除算法和复制算法的缺点。
缺点:
- 效率上标记-压缩算法不如复制算法。
- 移动对象的同时,如果被其他对象引用,则还需要修改引用地址。
- 移动的过程中需要停止用户线程(STW)。
4.垃圾清除阶段算法小节
标记-清除算法 | 标记-压缩(整理)算法 | 复制算法 | |
---|---|---|---|
效率 | 中 | 最慢 | 最快 |
空间开销 | 少(存在内存碎片) | 少(不会产生内存碎片) | 多(不会产生内存碎片) |
移动对象 | 否 | 是 | 是 |
上述回收算法各有优缺点,需要根据不同的场景选用不同的算法。因此出现了分代收集算法。
5.分代收集算法
概述:
不同对象的生命周期不同,所以根据不同的生命周期可以采用不同的收集算法。Java堆分为老年代和新生代,这样就可以根据各个分代的特点使用不同的回收算法,以便提高回收效率。
各个分代使用的回收算法和特点:
-
新生代:区域相对老年代较小,对象生命周期较短、存活率低,回收频繁。针对这种情况使用复制算法,速度是最快的。并且复制算法的效率和存活对象的数量有关,所以非常适合复制算法,使用两个Survivor的设计缓解复制算法内存利用率不高的问题(默认情况下只会空闲新生代中的十分之一的空间)。
-
老年代:区域比较大,对象生命周期较长、存活率高,回收频率不高。针对这种存在大量存活对象的空间,复制算法明显不合适。一般使用标记-清除算法或者标记-清除和标记-压缩算法的混合实现。目前Hotspot虚拟机采用的CMS回收器,基于这两种算法实现的。
- 标记(Mark)阶段的开销是和存活对象成正比的。(存活的对象多遍历的时间越长)
- 清除(Sweep)阶段的开销是与所管理区域的大小成正比的。(线性遍历)
- 压缩(Compact)阶段的开销与存活对象的数据成正比。(存活对象越多需要整理的也越多)