在进入JVM的GC机制之前我们需要先了解一些常见的垃圾判断算法以及垃圾回收算法,先对这些算法有个清晰的认知,再去了解JVM各个垃圾回收器是怎么使用这些算法进行GC的落地的。
常见的垃圾判断算法有两种:引用计数法,可达性分析算法。
常见的垃圾回收算法/理论有这几大类:标记清除算法,标记复制算法,标记整理算法,分代收集算法。
引用计数法
概述
所谓引用计数就是在这个对象信息中有一个专门的空间专门用于记录:其他引用或者其他对象对自己的指向次数。
如果该对象的这个计数为0,就可以判断这个对象是个垃圾对象。
图解
如图所示:
栈中的一个对象引用指向了堆中对象A,因此对象A的计数器为1
堆中的对象B,由于被对象A依赖又算上对象A被栈中引用指向,因此对象B的计数为2。
特点
优势
- 不会产生STW,可以立即回收垃圾。因为每个对象在被引用次数为0的时候,是立即就可以知道的,因此不需要额外开启一个GC线程进行垃圾回收,因此可以避免STW。
- 实现简单。
劣势
- 致命问题:对于循环依赖没办法进行垃圾判断,最终导致内存泄漏。
- 对于长链接引用的计数不方便,例如上述图中的B的计数需要递归到引用A(),最终才能得出正确的引用结果2。
循环引用图解
如图所示:
这里的对象A和对象B互相依赖因此计数器的值都是1。
此时无论是对象A还是对象B,都没有外部的引用指向它们了,也就是说它们因该是垃圾对象了。
由于引用计数算法中只有计数器值为0才会被判定为垃圾,这两个对象也就不会被GC回收,造成内存泄漏,最终导致OOM。
可达性分析算法
概述
可达性分析算法的流程是:
- 先找出JVM中的GC Root对象;
- 然后从GC Root对象开始根据依赖关系遍历依赖的对象并给这些对象打上标记;
- 当所有根据GC Root对象遍历的对象标记完后。堆中剩余的其他未被标记的对象就是垃圾对象。
GC ROOT对象
在概述中出现一个新的名词:GC ROOT对象,这里稍微解释一下。在JVM中GC ROOT对象往往指代以下几种对象:
- 类加载器对象。
- Thread对象。
- 虚拟机栈的本地变量表中的对象引用指向的对象。
- static成员变量引用的对象。
- 常量引用的对象。
- 本地方法栈的变量引用的对象。
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。
图解![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/00115213b5a298c52f956df1780f7934.png)
如图所示:
绿色对象就是被GC ROOT引用的对象,因此不是垃圾对象。
红色对象没有和任何GC ROOT关联,因此都是垃圾对象。
特点
优势
- 主要解决的,引用计数算法无法堆循环依赖对象的垃圾判断,避免了JVM层面会出现内存泄漏的问题。
劣势
- 产生了STW,相对于引用计数算法,对系统的开销更大。
由于可达性分析算法的流程比引用计数算法的流程复杂,往往需要新开一个GC线程专门用于垃圾检测和回收。
为了避免遍历对象时的误判情况发生,此时需要暂停工作线程,因此会导致STW。