强引用、软引用、弱引用和幻象引用的区别

>>最全面的Java面试大纲及答案解析(建议收藏)  

强引用

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾收器绝不会回收它。当JVM内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError运行时错误,使程序异常终止,也不会靠随意回收具有强引用对象来解决内存不足的问题。设置堆大小为100M:-Xmx100m

public class StrongReference {
   public static void main(String[] args) throws InterruptedException {
       byte[] bytes = new byte[1024 * 1024 * 20];
       System.gc();
       Thread.sleep(300);
       System.out.println(bytes == null);
       // 虚拟机内存
       Runtime rt = Runtime.getRuntime();
       long vmTotal = rt.totalMemory() / (1024 * 1024);
       System.out.println("JVM总内存空间为:" + vmTotal + " MB");
       byte[] bytes1 = new byte[1024 * 1024 * 90];
  }
}

输出结果如下:

强引用、软引用、弱引用和幻象引用的区别

从上图可以看出,结果输出false,说明该对象没有被垃圾回收。当堆空间不足时,抛出OutOfMemoryError异常,使程序停止。

软引用

软引用是用来描述一些还有用但并非必须的对象,在java.lang.ref包中用SoftReference类表示。对于软引用关联着的对象,只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象。在抛出OutOfMemoryError之前,清理软引用指向的对象。

应用场景: 软引用通常用来实现内存敏感的缓存。如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。

public class SoftReferenceTest {
   public static void main(String[] args) throws InterruptedException {
       //设置50M缓存数据
       byte[] data = new byte[1024 * 1024 * 50];
       //将缓存数据用软引用持有
       SoftReference<byte[]> cache = new SoftReference<>(data);
       //将缓存数据的强引用去除
       data = null;
       System.out.println("----回收前----");
       System.out.println(data);
       System.out.println(cache.get());
       System.gc();
       Thread.sleep(300);
       System.out.println("----回收后----");
       System.out.println(data);
       System.out.println(cache.get());
       //再分配一个60M的对象
       byte[] data1 = new byte[1024 * 1024 * 60];
       System.out.println("----分配后----");
       System.out.println(data);
       System.out.println(cache.get());
  }
}

输出结果如下:

强引用、软引用、弱引用和幻象引用的区别

从上图可以看出,当JVM内存空间足够时,垃圾回收器不会回收该对象,当内存空间不够的时候,才会对这个对象进行回收。

弱引用

弱引用在java.lang.ref包中用WeakReference类表示。弱引用的生命周期比软引用短。在垃圾回收时,不管当前JVM的内存空间是否足够,一旦发现具有弱引用的对象且没有任何强引用关联它,就会被回收。

public class WeakReferenceTest {

   public static void main(String[] args) throws InterruptedException{
       //设置50M缓存数据
       byte[] data = new byte[1024 * 1024 * 50];
       //将缓存数据用弱引用持有
       WeakReference<byte[]> cache = new WeakReference<>(data);
       System.out.println("----回收前----");
       System.out.println(data);
       System.out.println(cache.get());
       //将缓存数据的强引用去除
       data = null;
       System.gc();
       Thread.sleep(300);
       System.out.println("----回收后----");
       System.out.println(data);
       System.out.println(cache.get());
  }
}

输出结果如下:

强引用、软引用、弱引用和幻象引用的区别

从上图可以看出,当弱引用对象无其他指向它时,那么这个对象就会被垃圾回收。

幻象引用

幻象引用也叫虚引用,在java.lang.ref包中用PhantomReference类表示。无法通过幻象引用访问对象的任何属性或函数。如果一个对象仅持有幻象引用,那么他就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

public class PhantomReferenceTest {

   public static void main(String[] args){
       //设置50M缓存数据
       byte[] data = new byte[1024 * 1024 * 50];
       ReferenceQueue queue = new ReferenceQueue();
       //将缓存数据用幻象引用持有
       PhantomReference<byte[]> cache = new PhantomReference<>(data,queue);
       System.out.println(cache.get()==null);
  }
}

输出结果如下:

强引用、软引用、弱引用和幻象引用的区别

虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列ReferenceQueue联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是 否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

 — End —

原文始发于微信公众号(一盏红茶):强引用、软引用、弱引用和幻象引用的区别