强、软、弱、虚引用学习

Java中根据引用的内存敏感度和GC之间的关系把引用分成了四个级别:强、软、弱、虚

强引用

宁可OutOfMemory也不回收。看下面的代码:

// 用于占位
public class Holder {
	private static final int MB = 1024 * 1024;  
	private byte[] holder;
	public Holder() {
		this.holder = new byte[1 * MB];
	}
}
public class RefrenceLeaning {
	public static void main(String[] args) {
		/**
		 * 强引用,宁可内存溢出也不回收
		 * 只有当设置为power=null的时候才会在GC的时候被释放
		 * 
		 * 这个引起的问题是,当使用List等有自由内存结构的数据结构时,需要对用过的手工设置为null
		 */
		
		Holder d1 = new Holder();
		Holder d2 = new Holder();
		Holder d3 = new Holder();
		Holder d4 = new Holder();
		Holder d5 = new Holder();
	}
}

用VM参数:
-verbose:gc -Xms2m -Xmx2m -Xmn1m -XX:SurvivorRatio=8 -XX:+PrintGCDetails

来执行,会发现抛出内存溢出异常。要解决强引用可能带来的内存消耗,办法是使用之后设置为null。这种情况比较适用于使用有自己的内存结构的数据结构时:

比如如下的一个Queue:

class Queue{
	private Object[] data = new Object[24];
	int index = 0;
	public void put(Object o){
		data[index] = o;
		index++;
	}
	
	public Object getOne(){
		// 这里因为没有设置返回的Object为null,可能会造成内存泄露
		return data[index--];
	}
}

用刚才的Holder来解释一下就是这样子的:

public class RefrenceLeaning {
	public static void main(String[] args) {
		/**
		 * 强引用,宁可内存溢出也不回收
		 * 只有当设置为power=null的时候才会在GC的时候被释放
		 * 
		 * 这个引起的问题是,当使用List等有自由内存结构的数据结构时,需要对用过的手工设置为null
		 */
		
		Holder d1 = new Holder();
		d1 = null;
		Holder d2 = new Holder();
		d2 = null;
		Holder d3 = new Holder();
		Holder d4 = new Holder();
		Holder d5 = new Holder();
	}
}

这样就不会内存溢出了,说明会被回收

 

更好的办法是使用作用域来隐式的表示引用的失效,如下:

public class RefrenceLeaning {
	public static void main(String[] args) {
		/**
		 * 强引用,宁可内存溢出也不回收
		 * 只有当设置为power=null的时候才会在GC的时候被释放
		 * 
		 * 这个引起的问题是,当使用List等有自由内存结构的数据结构时,需要对用过的手工设置为null
		 */
		
		{
			Holder d1 = new Holder();
			Holder d2 = new Holder();
			Holder d3 = new Holder();
		}
		Holder d4 = new Holder();
		Holder d5 = new Holder();
	}
}

这样也不会内存溢出。

 

软引用

很有意思的类,  当一个对象只有软引用的时候,内存不足的时候会被GC掉。注意这个只有。如下:

public class ReferencePowser<T> {
	private T t;
	public ReferencePowser(T t) {
		this.t = t;
	}
}
public class RefrenceLeaning {
	public static void main(String[] args) {
	    /**
	      * 用作对比, d1在作用域执行完之后是可以被回收的了,但是因为有
          * ReferencePowser的存在,引用还是强的,所以不会被GC。
          **/
        ReferencePowser<Holder> rp1 = null;
        {
            Holder d1 = new Holder();
            rp1 = new ReferencePowser<Holder>(d1);
        }
        Holder d2 = new Holder();
        SoftReference<Holder> sr2 = new SoftReference<Holder>(d2);
        Holder d3 = new Holder();
        Holder d4 = new Holder();
        Holder d5 = new Holder();
    }
}

 

这里会出现内存溢出。 因为虽然d1在作用域中,但是还有ReferencePowser中的引用,因此仍然不能GC。 看软引用的情况:

public class RefrenceLeaning {
	public static void main(String[] args) {
		/**
		 * 软引用, 当一个对象只有软引用,内存不足的时候会被GC。
		 * 
		 */
		SoftReference<Holder> sr1 = null;
		{
		    Holder d1 = new Holder();
		    sr1 = new SoftReference<Holder>(d1);
		}
        Holder d2 = new Holder();
		SoftReference<Holder> sr2 = new SoftReference<Holder>(d2);
        System.gc();
        try {
            Thread.sleep(1000);
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("d1=" + sr1.get());
		Holder d3 = new Holder();
		Holder d4 = new Holder();
		Holder d5 = new Holder();
		System.out.println("d1=" + sr1.get());
		System.out.println("d2=" + sr2.get());
	}
}

将会打印如下:

d1=com.price.effective.create.Holder@ca0b6

d1=null
d2=com.price.effective.create.Holder@1b67f74

可见之照样被回收了。  这样就可以实现一些特别的场景。 比如如果这个对象一直内存还够,就不会回收,可以用sr1.get()拿回来,但是内存不够的时候可能需要重新创建。这样可以一定程度的起到缓存的作用。

 

弱引用

跟软引用的区别在于,只有软引用的时候,只要执行了GC都会被GC掉,不会等到内存不足

public class RefrenceLeaning {
	public static void main(String[] args) {
		/**
		 * 弱引用,只有弱引用的时候,只要进行GC,就会被回收,不会等到内存不够的时候
		 * 
		 */
		WeakReference<Holder> wr1 = null;
		{
			Holder d1 = new Holder();
			wr1 = new WeakReference<Holder>(d1);
		}
		Holder d2 = new Holder();
		SoftReference<Holder> sr2 = new SoftReference<Holder>(d2);
		System.gc();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("d1=" + wr1.get());
		Holder d3 = new Holder();
		Holder d4 = new Holder();
		Holder d5 = new Holder();
		System.out.println("d1=" + wr1.get());
		System.out.println("d2=" + sr2.get());
	}
}

打印如下:

d1=null
d1=null
d2=com.price.effective.create.Holder@a62fc3
可以看出来,第一次GC的时候内存还够,但是已经被GC掉了。

 

虚引用

又叫幽灵引用,很少用到,只有虚引用的时候垃圾回收的时候是根本不会考虑这个引用的,该干啥干啥。

一般跟ReferenceQueue一起实现细粒度的内存控制。 当虚引用的对象GC后,这个虚引用会被放入到queue中,这样可以监控这个queue做一些特别的事情。从网上找到的一段代码很好。用来判断只有当内存回收之后才分配新的对象:

http://blog.youkuaiyun.com/imzoer/article/details/8044900

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值