垃圾回收算法
标记-清楚算法(Mark and Sweep)
- 😀 标记:从根集合进行扫描,对存活的对象进行标记。
- 😀 清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存
- - 在Mark阶段 根对象可以访问b,b又可以访问e,故be都是可到达对象,同理f,g,j,k都是可达对象
- 未被标记未可达对象的都会被清除
🐖意:在垃圾回收阶段,应用程序的执行会暂停,等待回收执行完毕后,再恢复程序的执行。
不足:碎片化(如图所示,A,C,D,I被清除了,内存空间不在连续的内存碎片,内存碎片太多会导致以后在程序运行过程中需要分配较大的内存时,不得不再次的进行垃圾回收,程序会暂停,垃圾回收一直在回收垃圾,最后会导致内存溢出)
复制算法(Copying)
- 😂 分为对象面和空闲面
- 😂 对象在对象面创建
- 😂 存活的对象被从对象面复制的空闲面 (当定义为空闲面的内存快用完的时候,会把存活的对象复制到空闲面)
- 😂 将对象面所有内存清楚
优点:
- 😀 解决了碎片化问题
- 😀 顺序分配内存,简单高效
- 😀 适合用于对象存活率低的场景
缺点:
- 若对象存活率较高的场景,就会进行较多的复制操作,效率会很低
- 更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法
标记-整理算法(Compacting)
- 😀 标记:从根集合进行扫描,对存活的对象进行标记。
- 😀 清除:移动所有存活的对象,且按照内存地址以此排序,然后将末端内存地址以后的内存全部回收
会将没有标记的对象清除,然后所有存活的对象向一端移动,然后清理掉末端内存地址以外的内存
优点:
- 😀 避免内存的不连续性
- 😀 不用设置两块内存互换
- 😀 适用于存活率较高的场景
分代收集算法(Generational Collector)
- 😂 垃圾回收算法的组合拳
- 😂 按照对象生命周期的不同划分区域以采用不同的垃圾回收算法
GC 的分类
- Minor GC
- Full GC
年轻代:尽可能快速收集掉那些生命周期短的对象
- Eden 区
- 两个Survivor区
HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1,新创建的对象一般都会放在Eden区域,大对象特殊处理,经历过一次Minor GC 后,若还存活会放到幸存区,在经历Minor GC很多次后会存放到老年区
对象如何晋升到老年代
-
😀 经历一定Minor GC次数依然存活的对象
-
😀 Survivor 区中存放不下的对象
-
😀 新生成的大对象(-XX:+PretenuerSizeThreshold )(超过这个size就会放入老年代)
常用的调优参数
-
-XX:SurvivorRatio: Eden和Survivor的比值,默认时8:1
-
-XX:NewRatio:老年代和年轻代内存大小的比例
-
-XX:+MaxTenuringThreshold:对象从年轻代晋升到老年代经过gc次数的最大阈值
老年代:存放生命周期较长的对象
- 触发Full GC的条件
- 老年代空间不足
- 永久代空间不足
- CMS GC时出现promotion failed和concurrent mode failure
- System.gc()方法的调用
- 统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间
- 触发Full GC的条件