标记算法
引用计数算法(不适合循环引用)
- 通过判断对象的引用数量来决定对象是否可以被回收
- 每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1
- 任何引用计数为0的对象实例可以被当做垃圾收集
优点: 执行效率高,程序执行受影响较小
缺点: 无法检测出循环引用的情况,导致内存泄露
可达性分析算法
通过判断对象的引用链是否可达来决定对象是否可以被回收
可以作为GCRoot的对象
垃圾回收算法
标记-清除算法(碎片化)
- 标记:从根集合进行扫描,对存货的对象进行标记
- 清除:对堆内存从头到尾进行线性遍历,回收不可达对象
缺点:不需要进行对象的移动,碎片化严重。如果下一次需要新建一个大的对象,位置不够
复制算法(年轻代)
- 分为对象面和空闲面
- 对象在对象面上创建,存活对象从对象面复制到空闲面,再将对象面的所有对象清除
- 解决了碎片化问题
- 顺序分配内存,简单高效
- 适用于对象存活率低的场景
- 适用于年轻代(存活对象少)
标记-整理算法(老年代)
- 标记:从根集合进行扫描,对存货的对象进行标记
- 清除:移动所有存活的对象,且按照内存地址依次排列,然后将末端内存地址以后的内存全部回收
- 避免内存的不连续
- 不用设置两块内存互换
- 适用于存活率高的场景,如老年代
分代收集算法
- 垃圾回收算法的组合拳
- 按照对象生命周期的不同划分区域采用不同的垃圾回收算法
- 目的:提高JVM的回收效率
GC的分类
- Minor GC
发生在年轻代,采用复制算法
- Full GC
与老年代相关,一般老年代的回收伴随着年轻代的回收,因此称为FullGC
年轻代:尽可能快速地收集掉那些生命周期短的对象
- Eden区(伊甸园)
- 两个Survivor区(from&to)
8:1:1
年轻代垃圾回收过程:
eden满了,触发mirror-gc,将存活对象复制到survivor
清理eden区
eden再次满了,将eden和s0中的对象拷贝到s1,年龄加1,清理eden和s0
eden再次满了,将eden和s1中的对象拷贝到s0,年龄加1,清理eden和s1
当对象的年龄超过一定岁数,变为老年代,可通过参数调节
对象如何晋升到老年代
- 经历一定Minor次数仍然存活
- Survivor区中放不下的对象
- 新生成的大对象(-XX:+PretenuerSizeThreshold)
常用调优参数
老年代:存放生命周期较长的对象
大概比例1:2
- FullGC和MajorGC
- FullGC比MinorGC慢(10倍),但执行频率低
触发fullGC方条件:
老年代空间不足
MinorGC晋升到老年代的平均大小大于老年代的剩余空间
永久代空间不足(JDK7之前,这也是用元空间替代永久代的原因,降低fullGC的频率)
调用System.gc()(这个方法不一定执行)
CMS GC时出现promotion failed,concurrent mode failure
使用RMI来进行RPC或者管理的JDK应用,每小时执行一次FullGC
垃圾收集器
- stop-the-world
- Safepoint(回收垃圾要提醒不要扔垃圾)
- JVM的运行模式
- Server(启动慢,稳定后比Client快)
- Client
垃圾收集器的联系
年轻代常见收集器
除了Serial只有它能和CMS配合,Server下的首选的收集器
老年代常见收集器
对停顿不敏感,硬件好,更多存活时间长的对象选择CMS;碎片化
GC常见面试题
Object的finalize()方法的作用是否与C++的析构函数作用相同
1 | public static void main(String[] args) { |