垃圾检测算法
引用计数法:实例每被引用一次,计数器+1,不再被引用,计数器-1。遍历堆中全部实例,计数器为0的,就是垃圾。
可达性分析法:从GCRoot(GC起点)出发,遍历实例,能访问到的就不是垃圾,访问不到的就是垃圾。
GC Roots
- 局部变量表:正在执行的函数参数,临时变量,临时值
- 方法区中的静态变量
- 方法区中的常量池:如常量池中的常量引用实例
- 本地方法栈中的变量:JNI调用Native方法所引用的实例
- 同步锁持有的对象
垃圾回收算法
复制算法(标记复制法)
将一块区域一分为二,区域内实例标记为可回收与不可回收,执行算法后将不可回收实例置入二区域,一区域全部空置。
优点:保证空间连续、算法实现逻辑相对简单
缺点:浪费空间
标记清除法
直接将内存中标记为可回收的实例释放掉
优点:算法效率高,实现相对简单
缺点:不保证空间连续性,空间内碎片化会越来越严重
标记整理法
与标记清除法相同,只是回收后会整理内存空间,保证连续性
缺点:效率低于标记清除,算法实现难度较大
不能说标记整理就一定比标记清除更优秀,更不能说标记整理是对标记清除的改进,选择那种算法,取决于对内存碎片和停顿时间中的哪一个容忍性更低
HotSpot中的垃圾回收器
串行回收器:Serial、SerialOld
- Serial是JDK1.3之前,年轻代唯一的回收器
- Client模式下的默认垃圾回收器
Serial采用复制算法,负责YoungGC;SerialOld采用标记整理算法,负责OldGC
并行回收器:ParNew、ParallelScavenge、Parallel old
- 提升GC的吞吐量
- Parallel是Java8的默认回收器
ParallelScavenge采用复制算法,负责YoungGC;ParallelOld采用标记整理算法,负责OldGC
并发回收器:CMS、G1、ZGC
- G1是Java9的默认回收器,ZGC是Java11引入
- G1和ZGC都不再基于分代回收模型
- CMS主要用于老年代,也可用于年轻代
- CMS采用标记清除算法
CMS的三次标记
- 初始标记:会STW,但是只标记到GCRoot是直接引用的实例,所以STW很短暂
- 并发标记:从初始标记的对象开始,遍历全部关联实例,耗时最久,但可以与用户线程并发执行,不会STW
- 重新标记:会STW,修正并发标记时,用户线程导致标记不准确的情况,也就是脏实例问题
G1分区回收(jdk1.9)
- 将内存划分为多个大小相等的独立区域,这些区域包含了逻辑上的年轻代和老年代
- Humongous,分代回收中没有此区域,专门用来存储大实例
- 区域之间是复制算法,但整体可以看做是标记整理算法
- 可以只针对部分区域进行垃圾回收,所以可以与用户线程交替进行,缩短STW
- 可以设置一定时间内,允许垃圾回收的时间,来实现可预测的STW