JVM高级特性与最佳实战(四)————垃圾收集算法

推荐阅读:

JVM高级特性与最佳实战(一)————JAVA内存区域
JVM高级特性与最佳实战(二)————对象的创建过程,内存布局,访问定位
JVM中字符串常量池的详细剖析
JVM高级特性与最佳实战(三)————如何判断对象已死?

引言

在讨论垃圾回收算法之前,我必须得补充下昨天我们讨论的JVM高级特性与最佳实战(三)————如何判断对象已死?。我们先来看下就算在不可达的队列中,对象如何活过来?请看代码:

package JVM高级特性与实战.对象死里逃生;

/**
 * Author:haozhixin
 * Func:  对象在垃圾收集标记后的死里逃生
 * Date:  20190817
 */
public class AliveFormGC {
	public static AliveFormGC SAVE_HOOK=null;

	public void isAlive(){
		System.out.println("oh  im alive now");
	}
	@Override
	protected void finalize() throws Throwable{
		super.finalize();
		System.out.println("finalize method executed!!");
		//重新赋引用
		AliveFormGC.SAVE_HOOK= this;
		System.out.println("1.SAVE_HOOK  value is :"+SAVE_HOOK);
	}

	public static void main(String [] args) throws Throwable{
		SAVE_HOOK = new AliveFormGC();

		SAVE_HOOK=null;
		//调用finalize方法。
		System.gc();
		//因为finalize优先级低,请下边的方法稍微等一下。
		Thread.sleep(500);
		//第一次竟然活过来了?
		System.out.println("2.SAVE_HOOK  value is :"+SAVE_HOOK);
		if(SAVE_HOOK!=null){
			SAVE_HOOK.isAlive();
			System.out.println("3.SAVE_HOOK  value is :"+SAVE_HOOK);
		}else {
			System.out.println("oh  im dead now");
			System.out.println("4.SAVE_HOOK  value is :"+SAVE_HOOK);
		}


		SAVE_HOOK=null;
		System.gc();
		Thread.sleep(500);
		//第二次没有活过来?
		//是因为任何一个对象的finalize方法都只会被系统自动调用一次。面临下次回收的时候,finalize方法不会再被执行
		System.out.println("5.SAVE_HOOK  value is :"+SAVE_HOOK);
		if(SAVE_HOOK!=null){
			SAVE_HOOK.isAlive();
			System.out.println("6.SAVE_HOOK  value is :"+SAVE_HOOK);
		}else {
			System.out.println("oh  im dead now");
			System.out.println("7.SAVE_HOOK  value is :"+SAVE_HOOK);
		}
	}
}

运行结果:

finalize method executed!!
1.SAVE_HOOK  value is :JVM高级特性与实战.对象死里逃生.AliveFormGC@1b87e2a
2.SAVE_HOOK  value is :JVM高级特性与实战.对象死里逃生.AliveFormGC@1b87e2a
oh  im alive now
3.SAVE_HOOK  value is :JVM高级特性与实战.对象死里逃生.AliveFormGC@1b87e2a
5.SAVE_HOOK  value is :null
oh  im dead now
7.SAVE_HOOK  value is :null

思路分析我写到代码备注里了,大家仔细分析,下面我们言归正传,继续来讲垃圾回收算法。

标记-清除算法

  • 思路
    算法分为标记和清除两部分,首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,他的标记过程就是对象的标记判定,这一点如果还有不清楚的去看我的博客补充下基础知识。JVM高级特性与最佳实战(三)————如何判断对象已死?
  • 特点
    (1)这个方法是最基本的算法,后续的收集算法都是基于这个标记清除算法演变的。
    (2)标记和清除的效率太低,影响回收速度。
    (3)标记清除后会产生大量的不连续的内存碎片。如果碎片过多,在进行下一次大对象的内存分配时,内存不够会提前触发另一次的垃圾回收动作。

复制算法

  • 思路
    他将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
  • 特点:
    (1)每次都是对整个半区进行内存回收,内存分配时不用考虑内存碎片等复杂情况。
    (2)实现思路简单,而且运行高效。
    (3)但是每次都把内存分为两块,可用的却只有一块,代价有点大。
    (4)如果对象存活率较高的时候,要进行较多的复制,效率也会变的低。
  • 优化
    IBM公司的专门研究表明,新生代中的对象98%都是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间。而是将内存分为一块较大的Eden空间和两块较小的Survivior空间,每次使用Eden和其中的一块Survivor空间。当回收的时候,将Eden和Survivior的存活的对象一次性复制到另外一块Survivior,然后清理之前的Eden和Survivor。
    这里我提一下,当Survivor内存不够了,会跟其他内存(老年代)进行内存的分配担保,至于怎么实现的这里不多提了。

标记-整理算法

  • 思路
    标记的过程与标记-清除算法一致,但是后续步骤增加了使对象向一端移动,直接清理另外一端的内存。
  • 特点
    主要针对老年代

分代收集算法

  • 思路
    根据对象的存活周期将内存分为几块。一般是把JAVA堆分为新生代和老年代。根据各个年代的特点选择合适的算法,比如,在新生代中,每次垃圾回收都发现有大批的对象死去,只有少量存活,那就选用复制算法。而老年代因为对象存活率高,没有额外空间进行分配担保,所以要使用标记-清除, 标记-整理的算法。

总结

大家主要想明白理论就可以了,一般的JAVA面试中也不会对虚拟机进行代码级深入的提问。下一节我们讲解下当前主流的垃圾收集器。


作者:select you from me
来源:CSDN
转载请联系作者获得授权并注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值