垃圾回收简称GC,新生代的垃圾回收一般称作Minor GC,老年代的垃圾回收一般称作Major GC或者Full GC。
在垃圾回收时,一般会伴随着应用程序的暂停运行,除了GC所需的线程外,其他线程都会处于等待状态,我们做GC调优就是要减少这个等待时间。
JVM垃圾回收的常见算法有:根搜索算法、标记-清除算法、复制算法、标记-整理算法和增量回收算法。
1. 根搜索算法
从一个节点GC ROOT开始(一个可以从堆外访问的对象),寻找对应的引用节点,然后继续寻找这个节点的引用节点,直到所有引用节点查找完毕后,就形成了一张图的结构。剩余节点被视为没有用到的节点,即无用节点,然后对这些节点进行垃圾回收。
如下图中橙色的节点就是要被回收的节点,因为它没有再被根节点引用到了。
在JVM中可以作为GC ROOT节点的对象总结有如下几类:
- JVM虚拟机栈中引用的实例对象
- 方法区中静态属性引用的对象(仅仅针对JDK1.8之前的JVM,JDK1.8以及之后的版本由于不存在方法区,静态属性直接存在Heap中)
- 方法区中静态常量引用的对象(仅仅针对JDK1.8之前的JVM,JDK1.8以及之后的版本由于不存在方法区,静态常量直接存在Heap中)
- 原生方法(native method,多用在JNI接口调用中)栈中引用的对象
- JVM自身持有的对象,比如启动类加载器、系统类加载器等
2. 标记-清除算法
从GC ROOT节点开始扫描,对存活的对象节点进行标记,标记完成后再次扫描整个内存区域,对未被标记的对象进行直接回收。
标记-清除算法回收完毕后,并不会对存活的对象进行移动整理,所以很容易导致内存碎片。由于这个算法是仅对未存活对象进行处理的,所以在不存活对象很少的情况下,性能极高。
我画了如下的步骤图,帮助理解。
1、扫描
2、标记
3、回收
3. 复制算法
- 复制算法是将内存分为两个区域,其中一个区域是全部空闲的,所有动态分配的实例对象都放在另外一个的区域里。
- 同样也是先从GC ROOT扫描,将被引用的节点标记出来
- 然后把被标记的节点复制到空闲区域里
- 最后清空原来放对象的区域
每次GC就是在重复这样的操作,每次都会有一个区域是空闲的。
4. 标记-整理算法
标记-整理算法采用的是和标记-清除算法一样的方式进行对象的标记、清除,但是标记-整理算法在回收不存活的对象占用的内存空间后,会将所有存活的对象向左端空闲空间移动,并更新对应的内存节点指针。
5. 增量回收算法
增量回收算法就是把JVM的内存空间划分为多个区域,每次仅对其中的某个区域进行垃圾回收,这样的好处就是可以减小应用程序的中断时间,用户一般都不能察觉到垃圾回收器在工作。