JVM垃圾收集机制
JVM结构
如何判定对象可回收
-
引用计数
- 缺点
- 很难解决对象之间循环引用的问题
- 缺点
-
可达性分析(根节点枚举)
- 使用引用链和GC Roots
- GC停顿原因
- 根节点枚举时必须保证对象引用关闭保持不变,所以必须停顿所有java线程
- 可以作为GC Roots的对象
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
垃圾收集算法
- 标记-清除算法
- 算法
- 标记需要回收的对象,标记完后统一清除
- 缺点
- 标记清除的过程效率不高
- 容易发生内存碎片
- 算法
-
复制算法
- 算法
- 将内存分为两个相同大小的块,每次只使用其中一块,当一块用完时就将存活的对象复制到另一块,然后清空已用过的那块
-
缺点
- 将内存缩小为原来的一半
- 如果对象存活率较高时,会发生大量的复制
-
使用场景
- 常使用回收新生代内存,HotSpot实现方式,将新生代内存分为:一块较大的Eden空间和两个较小的Survivor空间。每次使用Eden和其中一个Survivor空间,内存回收时将存活对象复制到另一个Survivor空间,如果另一个Survivor空间不够,则将存活对象复制到老年代。默认Eden空间和Survivor空间比例为8:1。
- 算法
-
标记-整理算法
- 算法
- 将存活的对象向一端移动,直接清理掉端边缘以为的内存
- 使用场景
- 常使用与清理老年代内存
- 算法
垃圾收集器
-
Serial收集器
-
特点
- 单线程
- 简单
- 对于单个CPU来说没有线程交互开销
-
缺点
- 执行垃圾回收时需要停掉所有用户线程
- 使用算法
- 新生代使用复制算法
- 老年代使用标记-整理算法
-
-
PerNew收集器
- 特点
- 使用多线程进行垃圾收集,默认线程数量等于CPU的数量
- 常用于server模式下新生代的垃圾收集
- 为了降低gc停顿时间
- 特点
-
Parallel Scavenge收集器
- 特点
- 新生代垃圾收集器,使用复制算法,且多线程收集垃圾
- 为了达到一个可控的吞吐量, 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
- 高的吞吐可以高效利用CPU时间,停顿时间越短,吞吐量越大
- 特点
-
Serial Old收集器
- 特定
- Serial Old收集器是 Serial收集器的老年带版本
- 单线程,使用“标记整理”算法
- 用途
- 与Parallel Scavenge收集器搭配使用
- 作为CMS收集器的后备预案,在并发收集器发现Concurrent Mode Failure时使用
- 特定
-
Parallel Old收集器
- 特点
- Parallel Scavenge收集器的老年代版本
- 使用标记-整理算法
- 特点
-
CMS收集器
-
特点
- 为获取最短回收停顿时间为目标的收集器
-
步骤
- 初始标记
- 标记GC Roots关联的对象,速度很快,需要停止用户线程
- 并发标记
- 重新标记
- 并发清除
- 初始标记
-
缺点
- CMS收集器对CPU资源比较敏感
- CMS收集器无法出炉浮动垃圾。浮动垃圾:CMS并发清理阶段用户线程还在运行,伴随程序的运行还会产生新的垃圾,这部分垃圾出现在标记过程之后,CMS无法再当次收集中处理掉它们。
- CMS是基于标记-清除算法的收集器,会产生内存碎片。
-
-
G1收集器
-
特定
- 并行与并发
- 分代收集
- 空间整合(整理来看:标记-整理算法,局部来看:复制)
- 可预测的停顿
- 降低停顿时间
- 建立可预测的停顿时间模型
-
步骤
-
初始标记
- stop the world(用户线程需要停止)
- 标记GC Roots能直接关联的对象
-
并发标记
- 从GC Roots开始对堆中的对象进行可达性分析,找出存活对象
- 与用户线程一起运行,此阶段比较耗时
- 最终标记
- stop the world(用户线程需要停止)
- 修正并发标记期间因用户线程运行而导致标记产生变动的对象的标记记录
- 筛选回收
- 对各个Region的回收价值和成本进行排序
- 根据用户期望的GC停顿时间制订回收计划
-
- 优点
- G1在回收内存后会马上同时做合并空闲内存的工作、而CMS默认是在STW(stop the world)的时候做
- G1可以通过设置预期停顿时间(Pause Time)来控制垃圾收集时间避免
- G1通过将内存空间分成区域(Region)的方式避免内存碎片问题
-
JVM配置信息
-
查看JVM配置
java -XX:+PrintCommandLineFlags -version
输入结果:
-XX:InitialHeapSize=2113761600 -XX:MaxHeapSize=32125009104 -XX:+PrintCommandLineFlags -XX:+UseCompressedOops -XX:+UseParallelGC java version "1.7.0_55" Java(TM) SE Runtime Environment (build 1.7.0_55-b13) Java HotSpot(TM) 64-Bit Server VM (build 24.55-b03, mixed mode)
-
修改JVM配置
JAVA_OPTS="-XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:../logs/gc.log"
-
JVM常用参数
- NewRatio
- 新生代(Eden+2*Survivor)和老年代的比例(不包含永久代)
- 例如 NewRatio=2 新生代:老年代=1:2 ,即新生代占对内存的 1/3
- SurvivorRatio
- 两个SurvivorRatio和Eden的比
- 例如SurvivorRatio=8,两个SurvivorRatio:Eden=2:8,即SurvivorRatio占年轻代的1/10
- UseParNewGC
- 打开此开关后,使用ParNew + Serial Old的收集器组合进行内存回收
- UseConcMarkSweepGC
- 打开此开关后,使用ParNew + CMS + Serial Old的收集器组合进行内存回收。Serial Old作为CMS出现Concurrent Mode Failure失败后的后备收集器使用
-
UseParallelGC
- 虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scanvenge + Serial Old (PS MarkSweep)的收集器组合进行内存回收
-
UseG1GC
- 使用G1收集器
- NewRatio
Hotspot堆内存结构
常见内存溢出问题
- Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
参考资料
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/13786.html