Android开发

软引用/弱引用/虚引用 源码分析及示例

2021-07-11  本文已影响0人  两份方糖红茶

本篇文章的目的是要总结在Java当中常见的引用与垃圾回收之间的关系,主要介绍的包括引用的概念,引用队列的概念,弱引用,软引用以及虚引用的原理,使用场景。

在进行引用的学习当中,需要区分被引用对象,引用对象之间的区别哦。

Reference基础知识

Reference对象通过泛型存储了对其他对象的引用,之所以需要通过这种方式来创建一个引用对象,是因为可以通过refrence对象建立其存储的对象与垃圾回收之间的关系,能够做到当虚拟机进行内存回收时,不同的reference类型能够影响该对象是何时被系统回收。

reference类当中的几个重要的变量及方法:

    volatile T referent; // 被建立引用的对象
    final ReferenceQueue<? super T> queue; // 建立该引用时关联的队列,在某些类型的引用种,这个可以为null,有些引用类型当中,必须包含引用队列

引用队列ReferenceQueue

为使介绍三种具体引用时更加清晰,这里先对上述提到的引用队列做介绍

了解以上基础概念之后,便可以介绍继承自reference抽象类的三种类:软引用、弱引用以及虚引用对于其关联的对象的回收状态的管理以及其与引用队列之间的关系。

三种引用类型介绍

软引用(SoftReference)

当一个对象只有软引用的时候,vm只会在系统内存不够的时候对其被引用对象进行回收。

虚引用(PhantomReference)

虚引用并不关心每个对象的生命周期,它没有对该对象建立任何引用,该对象随时可以被gc回收,而他唯一的作用就是跟踪该对象是否被回收了

弱引用(WeakReference)

当一个对象只有弱引用的时候,只要vm扫到这一片区域且并不关心当前内存是否不足,都会被系统进行回收,弱引用的对象较之软引用相比有更脆弱的生存周期,但是相对来说,比虚引用的生命周期强一点

弱引用通常被用于WeakHashMap,以及ThreadLocal当中

WeakHashMap
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
    V value;
    final int hash;
    Entry<K,V> next;

    /**
     * Creates new entry.
     */
    Entry(Object key, V value,
          ReferenceQueue<Object> queue,
          int hash, Entry<K,V> next) {
        super(key, queue);
        this.value = value;
        this.hash  = hash;
        this.next  = next;
    }

    @SuppressWarnings("unchecked")
    public K getKey() {
        return (K) WeakHashMap.unmaskNull(get());
    }

}
    private void expungeStaleEntries() {
        for (Object x; (x = queue.poll()) != null; ) {
            synchronized (queue) {
                @SuppressWarnings("unchecked")
                    Entry<K,V> e = (Entry<K,V>) x;
                int i = indexFor(e.hash, table.length);

                Entry<K,V> prev = table[i];
                Entry<K,V> p = prev;
                while (p != null) {
                    Entry<K,V> next = p.next;
                    if (p == e) {
                        if (prev == e)
                            table[i] = next;
                        else
                            prev.next = next;
                        // Must not null out e.next;
                        // stale entries may be in use by a HashIterator
                        e.value = null; // Help GC
                        size--;
                        break;
                    }
                    prev = p;
                    p = next;
                }
            }
        }
    }
ThreadLocal

ThreadLocal提供的对与对象的管理,主要用于进行同一个对象在不同线程之间的数据管理,通过ThreadLocal管理的对象,会在每一个线程当中提供该对象的副本,而使得不同线程对于该对象的访问和更改完全隔离,我们知道如果不使用ThreadLocal,那么所有线程对于变量的访问,均是使用同一个对象,而如果不进行线程同步,则会使访问修改产生误差

ThreadLocal的使用则需要进行初始化,利用get和set进行访问

项目当中使用弱引用的例子

在我们项目当中有一个用于通知外部ListenerMgr,其主要作用便是进行通过注册所有的回调对象,完成对所有注册过的场景进行通知的作用,这个通知的场景,常用于发生在注册的数据model请求数据回来后,通知拿到了后台数据进行下一步操作

之前在进行组件化的过程中,为了进行跨module的数据通知,便想通过引用这个管理回调,但是由于不知道弱引用的原理,导致产生了bug,接下来利用以上弱引用的知识进行复盘

ListenerMgr源码

public class ListenerMgr<T> {
    private final ConcurrentLinkedQueue<WeakReference<T>> mListenerQueue = new ConcurrentLinkedQueue();

    public ListenerMgr() {
    }

    public void register(T listener) {
        if (listener != null) {
            synchronized(this.mListenerQueue) {
                boolean contain = false;
                Iterator iterator = this.mListenerQueue.iterator();

                while(iterator.hasNext()) {
                    T listenerItem = ((WeakReference)iterator.next()).get();
                    if (listenerItem == null) {
                        iterator.remove();
                    } else if (listenerItem == listener) {
                        contain = true;
                    }
                }

                if (!contain) {
                    WeakReference<T> weakListener = new WeakReference(listener);
                    this.mListenerQueue.add(weakListener);
                }

            }
        }
    }
    public void startNotify(ListenerMgr.INotifyCallback<T> callback) {
        、、、
      // 读取操作
                while(true) {
                    Object listenerItem;
                    do {
                        if (!var3.hasNext()) {
                            return;
                        }

                        WeakReference<T> aCopyListenerQueue = (WeakReference)var3.next();
                        listenerItem = aCopyListenerQueue.get();
                    } while(listenerItem == null);

                    try {
                        callback.onNotify(listenerItem);
                    } catch (final Throwable var8) {
                        var8.printStackTrace();
                        Log.e("crash", var8.toString(), var8);
                        if (isDebug) {
                            Handler handler = new Handler(Looper.getMainLooper());
                            handler.post(new Runnable() {
                                public void run() {
                                    throw new RuntimeException(var8);
                                }
                            });
                        }
                    }
                }
            } catch (Throwable var9) {
            }
        }

    }
跨组件的调用导致的问题

补充

上一篇 下一篇

猜你喜欢

热点阅读