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
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掉了。
d1=null
d2=com.price.effective.create.Holder@a62fc3
可以看出来,第一次GC的时候内存还够,但是已经被GC掉了。
虚引用
又叫幽灵引用,很少用到,只有虚引用的时候垃圾回收的时候是根本不会考虑这个引用的,该干啥干啥。
一般跟ReferenceQueue一起实现细粒度的内存控制。 当虚引用的对象GC后,这个虚引用会被放入到queue中,这样可以监控这个queue做一些特别的事情。从网上找到的一段代码很好。用来判断只有当内存回收之后才分配新的对象:
http://blog.csdn.net/imzoer/article/details/8044900