【JVM】垃圾回收机制
1. 如何判断对象是否应该回收
引用计数法
Java中,引用和对象是有关联的,如果要操作对象则必须用引用进行。
因此,很显然有一个很简单的方法是通过引用计数来判断一个对象是否可以回收。简单来说就是给对象添加一个引用计数器,每当有一个地方引用他,计数器加一,每当一个引用失效,计数器减一。
任何时刻计数器值为零的对象就是不可能再被使用的,那么这个对象就是可回收对象。
那为什么主流的Java虚拟机里都没有使用这种算法呢?其中最主要的原因是他很难解决对象之间相互循环引用的问题。
可达性分析(根搜索路径)
哪些对象可以作为GC Roots呢
- 虚拟机栈(栈帧中的局部变量表)中引用的对象;
- 方法区中类静态属性引用的对象;
- 方法区中常量引用的对象;
- 本地方法栈中JNI(Native方法)引用的对象。
2. 内存分配策略
- 对象优先在EDEN分配,如果EDEN区没有足够的空间进行分配,将会发起一次Minor GC;
- 大对象直接进入老年代,例如很大的数组;
- 长期存活的对象将进入老年代,默认活过15次gc;
- 动态对象年龄判定,在survivor区空间中相同年龄所有对象的大小大于survivor区的一半,年龄大于或等于该年龄的对象直接进入老年区;
- 空间分配担保,发生minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果这个条件成立,那么可以确保minor GC是安全的。如果担保失败,那么只好再失败后重新发起一次full GC。
3. 垃圾回收算法
- 标记清除
- 复制
- 标记整理
- 分代收集
4. 垃圾收集器简单梳理
几种常用的垃圾收集器
1. 串行垃圾回收器Serial
为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程。因此不适合服务器环境使用。
2. 并行垃圾收集器
多个垃圾回收线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理首台处理等弱交互场景。
3. CMS(并发标记清除)垃圾收集器
用户线程和垃圾收集线程同时执行(不一定是并行,可能是交替执行),不需要停顿用户线程,互联网公司多用他,适用于对响应时间有要求的场景。
以上三个小总结
4. G1垃圾收集器
将堆内存分割为不同的region,使eden、survivor、老年代的内存不再连续,并发地进行收集。
5. 对垃圾收集器的理解
1. 怎么查看默认的垃圾收集器?
2. 默认的垃圾收集器有哪些
3. 部分参数预先说明
4. Server/Client模式
6. 新生代垃圾收集器
1. Serial
2. ParNew
3. Parallel Scanvenge (JAVA 8 默认)
7. 老年代垃圾收集器
1. Parallel Old(Java 8 默认)
2. CMS(自动关联ParNew)
- 初始标记:只是标记一下GC Roots能直接关联的对象,速度很快,会暂停所有工作线程。
- 并发标记:进行GC Roots跟踪过程,和用户线程一起工作,不需要暂停工作线程。是主要的标记过程,会标记全部对象。
- 重新标记:为了修正在并发标记期间,因用户程序继续运行而导致的标记产生变动的那一部分对象的标记记录,仍然需要暂停所有工作线程。(由于并发标记时,用户线程仍然在工作,因此正式清理前,再做修正)。
- 并发清除:清除GC Roots不可达对象,和用户线程一起工作,不需要暂停工作线程。基于标记结果,直接清理对象。(由于耗时最长的并发标记和并发清除过程中,垃圾收集线程和用户线程在一起工作,因此总体看来CMS的垃圾回收和用户线程是一起并发执行的)。
- 优点:并发收集低停顿
- 缺点:
- 对CPU资源压力大:由于并发进行,CMS在收集垃圾时与应用线程还是会增加对堆内存的占用,也就是说,CMS必须在老年代堆内存用尽之前完成垃圾回收。否则当CMS回收失败时,将触发担保机制,启动的Serial Old回收器将会以STW的方式进行一次GC,从而造成较大的停顿时间。
- 标记清除算法无法整理空间碎片,老年代空间会随着应用时长被逐步耗尽,最后将不得不通过担保机制对堆内存进行压缩。CMS也提供了参数
-XX:CMSFullGCsBeforeCompaction
(默认0,即每次都进行内存整理)来制定多少次CMS手机之后进行一次压缩的Full GC。
3. Serial Old
8. 垃圾收集器配置代码总结
9. 如何选择垃圾收集器
垃圾收集器之间的关联:
10. 单独拎出来的G1
1. 原理
区域化的垃圾收集器
回收步骤
2. 参数
3. 对比CMS