Java 强引用|软引用|弱引用|虚引用

Java由于存在GC自动回收机制,所以在引用计数这一块的机制相对完善,Java将持有对象的引用按照强弱顺序划分为:强引用、软引用、弱引用、虚引用,引用使用得好可以让GC快速回收已经超过生命周期的实例,使机器的内存占用处于一个动态较低的水平,使用不当就会产生内存泄漏,导致机器内存消耗殆尽,宕机,我们要清楚的掌控自己编写的程序的特性,要让程序处于可控的状态,至少是当前认为可控的。下面我们先来了解下引用的分类,然后再去探究使用的场景。

1、强引用

我们平常使用的就是强引用,如:

Map<String, Object> param = new HashMap<String, Object>(16); 

形如上述的引用就是强引用了,只要引用还在有效生命周期内,GC就无法将引用对应的堆上的空间回收,当然引用本身也是无法回收的,强引用的使用和规则是不言而喻的。

2、软引用

先来看看软引用的使用方式:

Map<String, Object> param = new HashMap<String, Object>(16);
param.put("status", 1);
SoftReference<Map<String, Object>> softRef = new SoftReference<Map<String, Object>>(param);
System.out.println(softRef.get().get("status"));

使用方式多了一层,但是也好理解,软引用引用力度仅次于强引用的一种引用,只有在虚拟机认为内存不够时,软引用的引用即使还在生命周期内,软引用指向的堆空间依然被强行回收,这时调用get()返回的就是null,所以正常情况下,软引用的表现和强引用无异,只有在虚拟机认为内存吃紧的时候,软引用才会体现出他的特性。

3、弱引用

弱引用的使用方式:

Map<String, Object> param = new HashMap<String, Object>(16);
param.put("status", 1);
WeakReference<Map<String, Object>> weakRef = new WeakReference<Map<String, Object>>(param);
System.out.println(weakRef.get().get("status"));

弱引用的使用方式和软引用几乎一样,他是引用力度次于软引用的一种引用,弱引用对实例的引用不会被引用计数器计算在内,所以如果一个实例没有强引用的存在,只有弱引用的存在,他有可能返回null,取决于GC有没有进行回收,上述例子同时存在强引用和弱引用,我们来看下只存在弱引用的情况:

WeakReference<Map<String, Object>> weakRef = new WeakReference<Map<String, Object>>(new HashMap<String, Object>(16));
System.gc();
System.out.println(weakRef.get());

这时打印出来的时null,也就是说弱引用不对GC的引用技术有效,使用时注意这个特性。

4、虚引用

虚引用的使用方式和上面略有不同,当GC准备回收一个对象时,如果发现他还有虚引用,就会将这个虚引用加入到与之关联的引用队列中。《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版第3.2.3小节是有这么一段话,“虚拟引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用”

我们先来看看虚引用类型的定义:

public class PhantomReference<T> extends Reference<T> {

    public T get() {
        return null;
    }

    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}

我们看到他只有一个构造函数,且要指定一个引用队列,下面的代码展示了一个Demo:

ReferenceQueue<Map<String, Object>> q = new ReferenceQueue<Map<String, Object>>();
PhantomReference<Map<String, Object>> phantomRef = new PhantomReference<Map<String, Object>>(new HashMap<String, Object>(16), q);
System.gc();
Thread.sleep(1000);
System.out.println(phantomRef.get());
System.out.println(q.poll());

打印结果是null和PhantomReference的一个实例,验证了无法通过虚引用来取得一个对象实例和引用被加入到引用队列的事实。

下面我们会讨论下实际中如何使用这几种引用。

猜你喜欢

转载自blog.csdn.net/tales522/article/details/81157357