垃圾回收
如何判定对象为垃圾对象
- 引用计数法
在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器+1,当引用失效时,引用计数器-1。引用计数器为零时认为失效
这种方法解决不了相互引用的情况 虚拟机一般不用 - 可达性分析法
定义GCRoot(根节点),从GCRoot往下寻找,对象与gcroot没有引用链的认为可回收
可作为GCRoots的对象
- 虚拟机栈
- 方法区的类属性所引用的对象
- 方法区中常量所引用的对象
- 本地方法栈中引用的对象
打印gc信息jvm参数:
-verbose:gc -XX:+PrintGCDetails
如何回收
- 回收策略
- 标记-清除算法
标记判断为垃圾的对象为可回收状态,然后清除。
存在的问题:效率问题;空间问题(因为内存很分散造成) - 复制算法
- 标记-整理算法
- 分代收集算法
- 标记-清除算法
复制算法
- 堆
- 新生代
- Eden 伊甸园
- Survivor 存活区
- Tenured Gen
- 老年代
复制算法问题:内存利用率问题
为了解决这个问题,将内存分区
- 新生代
将Eden区中幸存的对象复制到Survivor中,然后清除整个Eden
一般认为能存活的对象占10%左右,当survivor中存不下时,就将对象移交老年代,由老年代做内存担保
标记-整理算法
对于老年代,可回收内存很小,复制算法回收效率不高
将需要回收的内存往一边移动,不需要回收的内存往另一边移动,然后将回收部分清除
分代收集算法
按不同年代内存区域选择对应的收集算法
何时回收
垃圾收集器
Serial收集器
最基本,发展最悠久
单线程垃圾收集器
虽然是单线程的,还会用在桌面应用中
ParNew收集器
复制算法(新生代收集器)
多线程收集器
与serial收集器的区别就是ParNew是多线程
关注点是提高回收效率
Parallel Scavenge 收集器
复制算法(新生代收集器)
多线程收集器
关注点是达到可控制的吞吐量
吞吐量:CPU用于运行用户代码的时间与CPU消耗的总时间的比值
吞吐量=执行用户代码的时间/(执行用户代码的时间+垃圾回收所占用的时间)
相关配置:
-XX:MaxGFPauseMillis垃圾收集器最大停顿时间-毫秒
-XX:CGTImeRatio吞吐量大小 只能设置(0,100)值越大吞吐量越大,默认最大值99
CMS收集器 Concurrent Mark Sweep
使用标记清除算法
标记、清除两个步骤都是并发执行
- 工作过程
- 初始标记
- 并发标记
- 重新标记
- 并发清理
- 优点
- 并发收集
- 低停顿
- 缺点
- 占用大量的CPU资源
- 无法处理浮动垃圾 (清理完后又产生的垃圾需要下一次gc时才能被清理)
- 出现Concurrent Mode Failure (内存过小)
- 空间碎片 (标记清除算法的问题)
G1收集器
- jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
- jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
- jdk1.9 默认垃圾收集器G1
jdk默认使用的收集器是根据jdk所处的环境决定的
当jdk检测到服务器内存大于2G cpu多核就会认为是server环境,就会默认使用Parallel Scavenge收集器,如果是client环境就会默认使用Serial收集器
优势
- 并行并发
- 分代收集(不再严格区分新生代、老年代,内存就区分为一块块的区域 记录在RmemberSet表中)
- 空间整合
- 可预测的停顿
步骤
- 初始标记
- 并发标记
- 最终标记
- 筛选回收
记录有引用的对象到RmemberSet表中,将没有引用的对象清除,这样能达到可预测的停顿
与CMS比较