美文网首页
Android 中的引用类型初探

Android 中的引用类型初探

作者: 轻微 | 来源:发表于2018-11-18 01:37 被阅读207次

    原始地址:Android 中的引用类型初探

    引用种类

    • 强引用:在 GC 中如果发现一个对象是可达的,那么 GC 在任何情况都不会回收这个对象

    • 软引用(SoftReference):在 GC 中如果发现一个对象是软可达的 。那么 GC 可以根据内存情况清除这些对象。并且保证在抛出 OutOfMemoryError 异常之前。所有的软引用的对象是已经回收过。

    • 弱引用(WeakReference):在 GC 中如果发现一个对象是软可达的,GC 会回收这些对象。

    • 虚引用(PhantomReference):在 GC 中如果发现是一个幽灵引用的时候,GC 会回收这些对象。

    总结: 强引用在任何情况都不会被回收。软引用在 GC 可以被回收。弱引用和虚引用在 GC 中会尽可能回收。

    GC 流程简介

    Android GC 主要分为 标记 和 清除 阶段、 通过定义两个 Bitmap, Live Bitmap 和 Mark Bitmap , 前者表示上次 GC 存活的对象。后者表示这次 GC 存活的对象。 Mark Bitmap 存在 而 Live Bitmap 不存在的为当前 GC 回收的对象。 GC 结束的时候将 Mark Bitmap 设置为 Live Bitmap。 不管并行还是串行GC, 或者 ART 的 GC 基本流程类似。

    Reference 状态。

    public abstract class Reference<T> {
        ...
        volatile T referent; 
    
        final ReferenceQueue<? super T> queue; 
    
        Reference queueNext;
    
        Reference<?> pendingNext;
        ...
    }
    

    参数介绍:

    • referent:引用对象, referent 回收的时候设置为 null。
    • queue :声明的队列。 不为空的时候,在 referent 被回收以后,最终 Reference 会被添加到队列中去。
    • queueNext :默认为 null, 在 Enqueued 状态表示同一个 queue 下,下一个 Reference 节点。
    • pendingNext:默认为 null ,在 Pending 的时候,表示下一个待处理 Reference 节点

    状态装换

    Reference 有 4 种状态 Active,Pending,Enqueued,Inactive

    image.png
    • queue 不为空:

    声明的时候默认为 Active 状态( queueNext 为空 ,pendingNext 为空 )。在 GC 发现 referent 对象可以被回收,回收 referent ,设置 referent 为 null , 将 Reference 放在 clear 队列当中。 状态为 Pending 状态( queueNext 为空 ,pendingNext 不为空 ),GC 会唤醒 ReferenceQueueDaemon 线程处理引用 clear 队列。 ReferenceQueueDaemon 处理 clear 队列。将 Reference 对象放到 queue 队列里面去。 状态为 Enqueued 状态( queueNext 不为空 ,pendingNext 为 Reference )。 当 queue 调用 poll() 将 Reference 获取出来。 状态为 Inactive( queueNext 为 ReferenceQueue.sQueueNextUnenqueued,pendingNext 为 Reference)。

    • queue 为空

    声明的时候默认为 Active 状态(queueNext 为空 ,pendingNext 为空 )。在 GC 发现 referent 对象可以被回收,回收 referent ,设置 referent 为 null 。状态为 Inactive (queueNext 为空 ,pendingNext 为空 )。

    Reference 处理流程。

    虚拟机启动

    虚拟机启动的时候会启动守护线程。

    public final class Daemons{
       public static void start() {
            ReferenceQueueDaemon.INSTANCE.start(); // 引用队列处理。 
            FinalizerDaemon.INSTANCE.start(); // 处理 finalize 线程
            FinalizerWatchdogDaemon.INSTANCE.start(); // 监听 finalize 方法超时。
            HeapTaskDaemon.INSTANCE.start();
        }
    }
    

    加载链接类

    在 虚拟机加载和链接类的时候,会对 Class 进行引用类型判断。

     */
    enum ClassFlags {
     ...
        CLASS_ISREFERENCE          = (1<<27), // class is a soft/weak/phantom ref
                                              // only ISREFERENCE is set --> soft
        CLASS_ISWEAKREFERENCE      = (1<<26), // class is a weak reference
        CLASS_ISFINALIZERREFERENCE = (1<<25), // class is a finalizer reference
        CLASS_ISPHANTOMREFERENCE   = (1<<24), // class is a phantom reference
    
     ...
    };
    
    static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,
        Method* meth)
    {
        ...
        if (dvmCompareNameDescriptorAndMethod("finalize", "()V", meth) == 0) {
    
            if (clazz->classLoader != NULL ||
                strcmp(clazz->descriptor, "Ljava/lang/Enum;") != 0)
            {
                SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
            }
        }
        ...
    }
    

    加载类的时候如果发现自定义了 finalize 方法, 那么会在 class 的 accessFlags 对象打上 CLASS_ISFINALIZABLE 标志。

    bool dvmLinkClass(ClassObject* clazz)
    {
        ...
     
        if (strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0) {
            /* Don't finalize objects whose classes use the
             * default (empty) Object.finalize().
             */
            CLEAR_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
        } else {
           
            if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISFINALIZABLE)) {
                SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
            }
    
            /* See if this class descends from java.lang.Reference
             * and set the class flags appropriately.
             */
            if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISREFERENCE)) {
                u4 superRefFlags;
    
            
                superRefFlags = GET_CLASS_FLAG_GROUP(clazz->super,
                        CLASS_ISREFERENCE |
                        CLASS_ISWEAKREFERENCE |
                        CLASS_ISFINALIZERREFERENCE |
                        CLASS_ISPHANTOMREFERENCE);
                SET_CLASS_FLAG(clazz, superRefFlags);
            } else if (clazz->classLoader == NULL &&
                    clazz->super->classLoader == NULL &&
                    strcmp(clazz->super->descriptor,
                           "Ljava/lang/ref/Reference;") == 0)
            {
                u4 refFlags;
    
                refFlags = CLASS_ISREFERENCE;
                if (strcmp(clazz->descriptor,
                           "Ljava/lang/ref/SoftReference;") == 0)
                {
                } else if (strcmp(clazz->descriptor,
                           "Ljava/lang/ref/WeakReference;") == 0)
                {
                    refFlags |= CLASS_ISWEAKREFERENCE;
                } else if (strcmp(clazz->descriptor,
                           "Ljava/lang/ref/FinalizerReference;") == 0)
                {
                    refFlags |= CLASS_ISFINALIZERREFERENCE;
                }  else if (strcmp(clazz->descriptor,
                           "Ljava/lang/ref/PhantomReference;") == 0)
                {
                    refFlags |= CLASS_ISPHANTOMREFERENCE;
                } else {
                    /* No-one else is allowed to inherit directly
                     * from Reference.
                     */
    //xxx is this the right exception?  better than an assertion.
                    dvmThrowLinkageError("illegal inheritance from Reference");
                    goto bail;
                }
    
                SET_CLASS_FLAG(clazz, refFlags);
            }
        }
       ...
        return okay;
    }
    
    

    链接类的时候:
    当一个 Class 是 SoftReference 或者它的派生类则它的 accessFlags 会被设置为 CLASS_ISREFERENCE
    当一个 Class 是 WeakReference 或者 它的派生类则它的 accessFlags 被设置为CLASS_ISREFERENCE | CLASS_ISPHANTOMREFERENCE
    当一个 Class 是 PhantomReference 或者 它的派生类则它的 accessFlags 被设置为 CLASS_ISREFERENCE | CLASS_ISPHANTOMREFERENCE
    当一个 Class 是 FinalizerReference 则它的 accessFlags 被设置为 CLASS_ISREFERENCE | CLASS_ISFINALIZERREFERENCE。 它没有派生类, 因为 FinalizerReference 是 Final 。
    当一个 Class 拥有自定义的 finalize()方法, 或者父类拥有finalize()方法, 那么就会被打上 CLASS_ISFINALIZABLE 标识。 这里有一个是例外 。Objectfinalize()是一个空实现。 它又是所有类的父类。 它会被清除 CLASS_ISFINALIZABLE 标识。因为如果不这样, 所有的类都将被打上 CLASS_ISFINALIZABLE

    对象初始化。

    /* File: c/OP_INVOKE_OBJECT_INIT_RANGE.cpp */
    HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
        {
         ...
             */
            if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) {
                EXPORT_PC();
                dvmSetFinalizable(obj);
                if (dvmGetException(self))
                    GOTO_exceptionThrown();
            }
    
         ...
            FINISH(3);
        }
    OP_END
    

    在类初始化的时候, 会根据 class 是否有 CLASS_ISFINALIZABLE, 即 拥有 自定义 finalize 方法。 那么会调用 dvmSetFinalizabledvmSetFinalizable 内部调用了 Java 的 FinalizerReference.add 方法。

    public final class FinalizerReference<T> extends Reference<T>{
        // This queue contains those objects eligible for finalization.
        public static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
    
        public static void add(Object referent) {
            FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue);
            synchronized (LIST_LOCK) {
                reference.prev = null;
                reference.next = head;
                if (head != null) {
                    head.prev = reference;
                }
                head = reference;
            }
        }
    }
    

    这里生成了一个新的引用 FinalizerReference 来持有对象,所有的FinalizerReference 设置同一个 queue 。同时将所有的 FinalizerReference 串联起来。

    GC

    标记动作就是从 根集 对象开始标记,在标记对象的时候,会根据对象的引用类型,添加到对应的引用队列中。

    *
     * Process the "referent" field in a java.lang.ref.Reference.  If the
     * referent has not yet been marked, put it on the appropriate list in
     * the gcHeap for later processing.
     */
    static void delayReferenceReferent(Object *obj, GcMarkContext *ctx)
    {
       ...
           if (pending == NULL && referent != NULL && !isMarked(referent, ctx)) {
            Object **list = NULL;
            if (isSoftReference(obj)) {
                list = &gcHeap->softReferences;
            } else if (isWeakReference(obj)) {
                list = &gcHeap->weakReferences;
            } else if (isFinalizerReference(obj)) {
                list = &gcHeap->finalizerReferences;
            } else if (isPhantomReference(obj)) {
                list = &gcHeap->phantomReferences;
            }
            assert(list != NULL);
            enqueuePendingReference(obj, list);
        }
    }
    

    接下来处理 4 种引用队列。 软引用, 弱引用, 虚引用, finalizer 引用队列

    void dvmHeapProcessReferences(Object **softReferences, bool clearSoftRefs,
                                  Object **weakReferences,
                                  Object **finalizerReferences,
                                  Object **phantomReferences)
    {
     ...
        /*
         * Unless we are in the zygote or required to clear soft
         * references with white references, preserve some white
         * referents.
         */
        if (!gDvm.zygote && !clearSoftRefs) {
            preserveSomeSoftReferences(softReferences);
        }
        /*
         * Clear all remaining soft and weak references with white
         * referents.
         */
        clearWhiteReferences(softReferences);
        clearWhiteReferences(weakReferences);
        /*
         * Preserve all white objects with finalize methods and schedule
         * them for finalization.
         */
        enqueueFinalizerReferences(finalizerReferences);
        /*
         * Clear all f-reachable soft and weak references with white
         * referents.
         */
        clearWhiteReferences(softReferences);
        clearWhiteReferences(weakReferences);
        /*
         * Clear all phantom references with white referents.
         */
        clearWhiteReferences(phantomReferences);
        /*
         * At this point all reference lists should be empty.
         */
    ...
    }
    static void clearWhiteReferences(Object **list)
    {
        GcMarkContext *ctx = &gDvm.gcHeap->markContext;
        size_t referentOffset = gDvm.offJavaLangRefReference_referent;
        while (*list != NULL) {
            Object *ref = dequeuePendingReference(list);
            Object *referent = dvmGetFieldObject(ref, referentOffset);
            if (referent != NULL && !isMarked(referent, ctx)) {
                /* Referent is white, clear it. */
                clearReference(ref);
                if (isEnqueuable(ref)) {
                    enqueueReference(ref);
                }
            }
        }
    }
    static void enqueueReference(Object *ref)
    {
        assert(ref != NULL);
        assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
        assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
        enqueuePendingReference(ref, &gDvm.gcHeap->clearedReferences);
    }
    
    

    对于弱引用,虚引用, 如果他们没有被标记,那么他们所持有的对象将会回收,referent 设置为 null 。 而他们本身根据 queue 是否为空进入不同状态, 为空将进入Inactive 状态。 不为空 进入Pending 状态。 所有的引用类型会被添加到 Clear 队列中。 此时加入的队列并不是他们自己的 queue 。
    对软引用来说. 并不会全部回收, 默认情况会回收一半。除非是即将发生 OOM 才会全部回收。这也是软引用和 弱引用,虚引用的主要区别。
    对于 FinalizerReferences 队列来说的话, 需要调用 enqueueFinalizerReferences 方法

    static void enqueueFinalizerReferences(Object **list)
    {
        GcMarkContext *ctx = &gDvm.gcHeap->markContext;
        size_t referentOffset = gDvm.offJavaLangRefReference_referent;
        size_t zombieOffset = gDvm.offJavaLangRefFinalizerReference_zombie;
        bool hasEnqueued = false;
        while (*list != NULL) {
            Object *ref = dequeuePendingReference(list);
            Object *referent = dvmGetFieldObject(ref, referentOffset);
            if (referent != NULL && !isMarked(referent, ctx)) {
                markObject(referent, ctx);
                dvmSetFieldObject(ref, zombieOffset, referent);
                clearReference(ref);
                enqueueReference(ref);
                hasEnqueued = true;
            }
        }
        if (hasEnqueued) {
            processMarkStack(ctx);
        }
    }
    

    由于还需要执行 finalizer 方法。 所以需要讲还没执行过 finalizer 方法并且未标记的对象标记,防止执行 finalizer 方法前对象被销毁了。然后将 Reference 添加到 Clear 队列。

    注: 将 Reference 加入自身的 queue 方法 和 finalizer 方法均不在 GC 过程中调用。因为 GC 时间是宝贵的。

    处理后续 Clear 队列 交给了守护线程 ReferenceQueueDaemon 。

    
        private static class ReferenceQueueDaemon extends Daemon {
            private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
    
            ReferenceQueueDaemon() {
                super("ReferenceQueueDaemon");
            }
    
            @Override public void runInternal() {
                while (isRunning()) {
                    Reference<?> list;
                    try {
                        synchronized (ReferenceQueue.class) {
                            while (ReferenceQueue.unenqueued == null) {
                                ReferenceQueue.class.wait();
                            }
                            list = ReferenceQueue.unenqueued; // 
                            ReferenceQueue.unenqueued = null;
                        }
                    } catch (InterruptedException e) {
                        continue;
                    } catch (OutOfMemoryError e) {
                        continue;
                    }
                    // 添加到自己的 queue
                    ReferenceQueue.enqueuePending(list);
                }
            }
        }
    

    ReferenceQueue.unenqueued 就是 Clear 队列。将引用添加到自己的 queue 里面。 状态由 Pending 变更为 Enqueued

    对于 FinalizerReference 对象的 finalize 方法。 它的处理交给 FinalizerDaemon

     private static class FinalizerDaemon extends Daemon {
    
            private final ReferenceQueue<Object> queue = FinalizerReference.queue;
            @Override public void runInternal() {
    
                while (isRunning()) {
                    try {
                        ...
                        FinalizerReference<?> finalizingReference = (FinalizerReference<?>)queue.poll();
                        ...
                        doFinalize(finalizingReference);
                    } catch (InterruptedException ignored) {
                    } catch (OutOfMemoryError ignored) {
                    }
                }
            }
            private void doFinalize(FinalizerReference<?> reference) {
                FinalizerReference.remove(reference);
                Object object = reference.get();
                reference.clear();
                try {
                    object.finalize();
                } catch (Throwable ex) {    
                } finally {
                    finalizingObject = null;
                }
            }
        }
    

    它是处理是从 FinalizerReference 的 queue 获取 FinalizerReference。 这里 queue 里面存的 FinalizerReference 已经是Enqueued 说明它持有的对象,已经应该需要销毁了。 所有获取的对象然后调用他们的 finalize 方法, 同时拦截所有的异常。并且不做处理。直接结束。 下次的 GC 就可以直接带走这些对象。

    相关文章

      网友评论

          本文标题:Android 中的引用类型初探

          本文链接:https://www.haomeiwen.com/subject/maigxqtx.html