对象被判定为垃圾的标准:没有被其他对象引用
判定对象是否为垃圾的算法
引用计数算法(非主流)
判断对象的引用数量
- 判断对象的引用数量来决定对象是否可以被回收;
- 每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1;(完成指值改变或生命周期过期);
- 任何引用计数为0的对象实例可以被当做垃圾收集;
**优点:**执行效率高,程序执行受影响较小;
**缺点:**无法检测出循环引用的情况,导致内存泄漏;(eg:父对象有一个对子对象的引用,子对象引用父对象)
可达性分析算法(主流算法)
通过判断对象的引用链是否可达来决定对象是否可以被回收(基于离散数学的图论)
可作为GC root的对象
- 虚拟机栈中引用的对象(栈堆中的本地变量表);
- 方法区中的常量引用的对象;
- 方法区中的类静态属性引用的对象;
- 本地方法栈中JNI(Native方法)的引用对象;
- 活跃线程的引用对象
垃圾回收算法
标记-清除算法(Mark and Sweep)
标记:从根集合开始扫描,对存活的对象进行标记;
清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存;
缺点:会产生大量不连续的碎片(碎片化)。
复制算法(Coping)
- 分为对象面和空闲面;
- 对象在对象面上创建;
- 存活的对象从对象面复制到空闲面;
- 将对象面所有对象内存清除;
优点:
- 解决了碎片化问题;
- 顺序分配内存,简单高效;
- 适用于对象存活率低的场景;
缺点
所需内存较大,适合新生代。
标记-整理算法(Compacting)
- 标记:从根集合进行扫描,对存活的对象进行标记;
- 清除:移动所有存活的对象,且按照内存地址次序排列,然后将末端内存地址以后的内存全部回收。
优点
- 避免内存的不连续行;
- 不用设置两块内存互换;
- 适用于存活率高的场景;
缺点
数据移动增加了CPU成本,适合老年代
分代收集算法(Generational Collector)
- 垃圾回收算法的组合拳;
- 按照对象生命周期的不同划分区域以采用不同的垃圾回收算法;
- 目的:提高JVM的回收效率。
GC分类
- Minor GC
- Full GC
词汇普及
新生代:主要是用来存放新生的对象。
Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
ServivorTo:保留了一次MinorGC过程中的幸存者。
ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。
MinorGC的过程:MinorGC采用复制算法。首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区);然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。
-XX:MaxTenuringThreshold 可设置新生代到老年代的年龄,默认15。
对象如何晋升到老年代
- 经历一定Minor次数依然存活的对象;
- Survivor存活不下的对象;
- 新生成的大对象(-XX:+PretenuerSizeThreshold)
常用的调优参数
- -XX:SurvivorRatio: Eden和Survivor的比值,默认是8(也就是8:1)
- -XX:NewRatio: 老年代和新生代内存大小的比例,默认为2(也就是2:1)
- -XX:MaxTenuringThreshold: 对象从新生代晋升到老年代经过GC次数的最大阈值(默认15,也就是所说的年龄)
老年代:主要存放应用程序中生命周期长的内存对象。
标记-清理算法或标记-整理算法
老年代的GC回收
- Full GC 和Major GC
- Full GC 比Minor GC慢,但执行频率低
Full GC是指对老年代和新生代都进行垃圾回收,Major GC有些解读中 == Full GC。
MajorGC采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。 当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。
触发Full GC的条件
- 老年代空间不足;(不要创建太大的对象)
- 永久代空间不足;(JDK6、7以前才有)
- CMS GC时出现promotion failed,concurrent mode failure
- Minor GC晋升到老年代的平均大小大于老年代的剩余空间(晋升过程时先检查晋升内容大小与老年代剩余内存大小)
- 调用System.gc() (只是提醒虚拟机,而不是绝对进行回收)
- 使用RMI进行RPC或管理的JDK应用,每小时执行1次Full GC
Major GC
永久代:
指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域. 它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。 在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。