Java 弱引用和软引用

如果一个对象仅仅是偶尔使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用 WeakReference 来引用该对象。

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

private static final ReferenceQueue<TestClass> QUEUE = new ReferenceQueue<>();

 WeakReference<TestClass> weakRef = new WeakReference<>(obj, QUEUE);

一般来说,很少直接使用WeakReference,而是使用WeakHashMap。在WeakHashMap中,内部有一个引用队列,插入的元素会被包裹成WeakReference,并加入队列中,用来做缓存再合适不过。

在Tomcat的缓存中,其实就用到了WeakHashMap:

public final class ConcurrentCache<K,V> {
    private final int size;
    private final Map<K,V> eden;
    private final Map<K,V> longterm;

    public ConcurrentCache(int size) {
        this.size = size;
        this.eden = new ConcurrentHashMap<>(size);
        this.longterm = new WeakHashMap<>(size);
    }

    public V get(K k) {
        // 先从eden中取
        V v = this.eden.get(k);
        if (v == null) {
            // 如果取不到再从longterm中取
            synchronized (longterm) {
                v = this.longterm.get(k);
            }
            // 如果取到则重新放到eden中
            if (v != null) {
                this.eden.put(k, v);
            }
        }
        return v;
    }

    public void put(K k, V v) {
        if (this.eden.size() >= size) {
            // 如果eden中的元素数量大于指定容量,将所有元素放到longterm中
            synchronized (longterm) {
                this.longterm.putAll(this.eden);
            }
            this.eden.clear();
        }
        this.eden.put(k, v);
    }
}

这里有eden和longterm的两个map,如果对jvm堆了解的话,可以看出tomcat在这里是使用ConcurrentHashMap和WeakHashMap做了类似分代缓存的操作。

在put方法里,在插入键值对时,先检查eden缓存的容量是否超出设定的大小。如果没有则直接放入eden缓存,如果超了则锁定longterm将eden中所有的键值对都放入longterm。再将eden清空并插入该键值对。

在get方法中,也是优先从eden中找对应的key,如果没有则进入longterm缓存中查找,找到后就加入eden缓存并返回。

经过这样的设计,相对常用的对象都能在eden缓存中找到,不常用(有可能被销毁的对象)的则进入longterm缓存。而longterm的key的实际对象没有其他引用指向它时,gc就会自动回收heap中该弱引用指向的实际对象,并将弱引用放入其引用队列中。

弱引用与软引用对比

弱引用与软引用的区别在于:

  1. 只具有弱引用的对象拥有更短暂的生命周期。
  2. 被垃圾回收器回收的时机不一样,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。而被软引用关联的对象只有在内存不足时才会被回收。
  3. 弱引用不会影响GC,而软引用会一定程度上对GC造成影响。

相似之处:都是用来描述非必需对象的。

那么什么时候用SoftReference,什么时候用WeakReference呢?

如果缓存的对象是比较大的对象,使用频率相对较高的对象,那么使用SoftReference会更好,因为这样能让缓存对象有更长的生命周期。

如果缓存对象都是比较小的对象,使用频率一般或者相对较低,那么使用WeakReference会更合适。

当然,如果实在不知道选哪个,一般而言,用作缓存时使用WeakHashMap都不会有太大问题

  • 弱引用是比软引用更弱的引用类型
  • 弱引用不能延长对象的生命周期,一旦对象只剩下弱引用,它就随时可能会被回收
  • 可以通过弱引用获取对象的强引用
  • 弱引用适合用作缓存

猜你喜欢

转载自www.cnblogs.com/light-zhang/p/9830480.html