强、软、弱、虚引用学习

简介: <p>Java中根据引用的内存敏感度和GC之间的关系把引用分成了四个级别:强、软、弱、虚</p> <h2>强引用</h2> <div>宁可OutOfMemory也不回收。看下面的代码:</div> <div><pre code_snippet_id="559687" snippet_file_name="blog_20141223_1_5988979" name="code" cl

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在作用域执行完之后是可以被回收的了,但是因为有
<span style="white-space:pre">	</span>  * ReferencePowser的存在,引用还是强的,所以不会被GC。
         **/
             ReferencePowser<Holder> rp1 = null;
    <span style="white-space:pre">	</span>    {
        <span style="white-space:pre">	</span>Holder d1 = new Holder();
       <span style="white-space:pre">		</span>rp1 = new ReferencePowser<Holder>(d1);
    <span style="white-space:pre">	</span>    }
    <span style="white-space:pre">	</span>    Holder d2 = new Holder();
    <span style="white-space:pre">	</span>    SoftReference<Holder> sr2 = new SoftReference<Holder>(d2);
    <span style="white-space:pre">	</span>    Holder d3 = new Holder();
    <span style="white-space:pre">	</span>    Holder d4 = new Holder();
    <span style="white-space:pre">	</span>    Holder d5 = new Holder();
    <span style="white-space:pre">	</span>}
}
 这里会出现内存溢出。 因为虽然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();
<span style="white-space:pre">		</span>try {
<span style="white-space:pre">			</span>Thread.sleep(1000);
<span style="white-space:pre">		</span>} catch (InterruptedException e) {
<span style="white-space:pre">			</span>e.printStackTrace();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>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.csdn.net/imzoer/article/details/8044900


相关文章
|
30天前
|
存储 缓存 监控
Linux内存管理:理解正常波动背后的机制
Linux内存管理:理解正常波动背后的机制
56 0
|
3月前
|
存储 算法 安全
清除你的烦恼!深入探讨垃圾回收算法、垃圾回收器和空间分配担保策略
清除你的烦恼!深入探讨垃圾回收算法、垃圾回收器和空间分配担保策略
|
3月前
|
缓存 算法 JavaScript
提高Java程序性能!了解可达性分析算法、强软弱虚引用和三色标记GC的过程,避免不可达对象阻碍程序性能!
提高Java程序性能!了解可达性分析算法、强软弱虚引用和三色标记GC的过程,避免不可达对象阻碍程序性能!
|
4月前
|
存储 监控 并行计算
线程操纵术之更优雅的并行策略
本文详细介绍了并行编程以及一些并行问题案例中的真实业务场景。
112422 1
|
8月前
|
Java
强、软、弱、虚,你是哪一种?
强、软、弱、虚,你是哪一种?
46 0
|
Java
强、软、弱、虚引用
强、软、弱、虚引用
65 0
METSO IOP303 可以通过线程的概念来缓解
METSO IOP303 可以通过线程的概念来缓解
74 0
METSO  IOP303 可以通过线程的概念来缓解
|
存储 监控 安全
ThreadLocal之强、弱、软、虚引用(下)
ThreadLocal之强、弱、软、虚引用
ThreadLocal之强、弱、软、虚引用(下)
|
缓存 安全 Java
ThreadLocal之强、弱、软、虚引用(上)
ThreadLocal之强、弱、软、虚引用
ThreadLocal之强、弱、软、虚引用(上)
|
存储 安全 Java
【新】虚拟机深层系「GC本质底层机制」SafePoint 的深入分析和底层原理探究指南
【新】虚拟机深层系「GC本质底层机制」SafePoint 的深入分析和底层原理探究指南
140 0