一、强引用:StrongReference
User user = new User();
一般的Object obj = new Object() ,就属于强引用。在任何情况下,只有有强引用关联(与根可达)还在,垃圾回收器就永远不会回收掉被引用的对象。
二、软引用: SoftReference
Object obj = new Object(); //强引用
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有时候会返回null
一些有用但是并非必需,用软引用关联的对象,系统将要发生内存溢出(OuyOfMemory)之前,这些对象就会被回收(如果这次回收后还是没有足够的空间,才会抛出内存溢出)。
可以存放一些频繁用到的数据,避免一直从内存中读取。
例如:一个程序用来处理用户提供的图片。如果将所有图片读入内存,这样虽然可以很快的打开图片,但内存空间使用巨大,一些使用较少的图片浪费内存空间,需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来,虽然内存占用较少,但一些经常使用的图片每次打开都要访问磁盘,代价巨大。这个时候就可以用软引用构建缓存。
三、弱引用: WeakReference
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾
一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。
注意:软引用 SoftReference和弱引用 WeakReference,可以用在内存资源紧张的情况下以及创建不是很重要的数据缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。
实际运用(WeakHashMap
、ThreadLocal
)
四、虚引用: PhantomReference
主要用于JVM虚拟机开发中,测试虚拟机的垃圾回收是否能正常执行。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
pf.get();//永远返回null
pf.isEnQueued();//返回是否从内存中已经删除
五、代码运行例子
首先要对VM 进行配置
-Xms10m
-Xmx10m
-XX:-PrintGC //打印GC日志
1)、SoftReference
public static void main(String[] argc){
Byte[] bytes = new Byte[1024 * 1024 * 500]; //new是强引用
SoftReference<Byte[]> sf = new SoftReference<>(bytes);
bytes = null; //干掉强引用,确保这个实例sf的软引用
System.out.println("Before gc ==============" + sf.get());
System.gc();//进行一次垃圾回收
System.out.println("After gc ==============" + sf.get());
LinkedList<byte[]> list = new LinkedList<>();
try {
for (int i =0; i < 100; i ++){
System.out.println("gc ==============" + sf.get());
list.add(new byte[1024 * 1024 * 800]);
}
} catch (Throwable e) {
System.out.println("Exception ==============" + sf.get());
}
}
根据打印的信息可以看到,只有当内存不足的情况下GC才会回收。
2)、WeakReference
public static void main(String[] argc){
Byte[] bytes = new Byte[1024 * 1024 * 500];
WeakReference<Byte[]> wf = new WeakReference<>(bytes);
bytes = null;
System.out.println("Before gc ==============" + wf.get());
System.gc();
System.out.println("After gc ==============" + wf.get());
}
从打印信息可以看到,只要遇到GC就会回收。
2)、PhantomReference
public static void main(String[] argc) throws InterruptedException {
ReferenceQueue<Object> rq = new ReferenceQueue<>();
Object o = new Object();
PhantomReference<Object> pf = new PhantomReference<>(o, rq);
o = null;
System.out.println("PhantomReference gc ==============" + pf.get());
System.out.println("Object gc ==============" + o);
System.out.println("ReferenceQueue gc ==============" + rq.poll());
System.gc();
Thread.sleep(200);
System.out.println("ReferenceQueue gc ==============" + rq.poll());
}
出打印信息可以看到,虚引用就是一个幽灵引用。主要用于JVM虚拟机开发中,测试虚拟机的垃圾回收是否能正常执行。
六、ReferenceQueue 讲解
对于软引用
和弱引用
,我们希望当一个对象被gc掉的时候通知用户线程,进行额外的处理时,就需要使用引用队列了。ReferenceQueue即这样的一个对象,当一个obj被gc掉之后,其相应的包装类,即ref对象会被放入queue中。我们可以从queue中获取到相应的对象信息,同时进行额外的处理。比如反向操作,数据清理等。
代码展示:
public static void main(String[] argc) throws InterruptedException {
ReferenceQueue<Object> queue = new ReferenceQueue<>();
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<>(obj, queue);
obj = null;
System.out.println("ReferenceQueue ----------->" + queue.poll());
System.out.println("WeakReference ----------->" + wf.get());
System.gc();
System.out.println("GC ----------->");
System.out.println("ReferenceQueue ----------->" + queue.poll());
System.out.println("WeakReference ----------->" + wf.get());
}