JVM - HotSpot
标记清除法
标记出不需要回收的对象,清除没有被标记的对象,它是最基础的收集算法,后续的算法都是对其的不足进行改进。
标记后会产生大量不连续的内存碎片。
标记复制法
将操作区域分为两块,每次只使用一块,当内存用尽会将存活对象复制到另一块区域,然后把它清理回收,减少回收区域。
标记整理法
标记出不需要回收的对象,然后往前移动,然后清理掉尾部的对象。
分代收集法(当前主流)
划分分新生代跟老年代(为了更好的回收内存),新生代又分 Eden,From Survivor,To Survivor,根据每个区域不同的情况提供不同的算法。
每个对象都会有个年龄计数器,首先会在Eden区域先分配,当没有空间时,虚拟机开始GC,如果对象还存活,年龄就会+1,同时 Survivor 区域能容纳下的话,就晋升到 Survivor 区域,当年龄大于15会晋升到老年代;
如果分配空间时,检测新生代的剩余空间比老年代的大,就会触发全局回收(full gc),System.gc() 也是触发的 full gc。
新生代中每次执行都会有大量对象回收,所以为了提高效率分配了标记复制法,而老年代存活率较高,所以分配了标记清除法跟标记整理法。
引用计数法
每个对象会有个计数器,每持有一个引用计数+1,引用失效,计数器-1,计数为0表示不在被使用。
引用计数法存在缺陷,无法解决循环引用问题(A,B两个对象互相引用),导致引用计数器不为0无法回收。
可达性分析法
生成一个叫 GC Roots 的对象作为起点,向下搜索,搜索路径叫引用链,无法通过引用链到达 GC Roots 起点的都是可能被回收对象;
GC Roots一般是同步锁持有对象,静态属性引用对象,常量引用对象等;
不可达对象会被标记一次,然后会判断是否有必要回收(是否调用 object 的 finalize 回收方法),如果被判定为有必要回收,会再次标记,然后执行回收。
finalize 方法官方已经不推荐,因为它是不可控的,无法立即执行gc。
引用
强引用:不会回收,直接抛异常;
软引用:空间不足时回收;
弱引用:被发现了就会被回收;
虚引用:随时可能会回收。
收集器
Serial(串行)收集器
单线程收集器,也是基础收集器,只会使用一条垃圾收集线程去完成垃圾收集工作,收集工作的时候必须暂停其他所有的工作线程,直到结束。
用户体验不佳,但是简单高效。
ParNew 收集器
多线程收集器,基于Serial收集器。
Parallel Scavenge 收集器
多线程收集器,提供了很多参数供用户找到最合适的停顿时间,适合自适应调节策略,也是JDK1.8的默认收集器。
CMS 收集器
单线程收集器,真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。