FinalizerReference 笔记

作者: yangweigbh | 来源:发表于2017-10-28 19:36 被阅读375次

    做内存泄漏分析时,总是可以在HeapDump中看到这么一个类:FinalizerReference

    数量很多,而且Retained Size还不小,那么他是内存泄漏的标志么?

    image.png
    template<bool kIsInstrumented, bool kCheckAddFinalizer>
    inline Object* Class::Alloc(Thread* self, gc::AllocatorType allocator_type) {
      CheckObjectAlloc();
      gc::Heap* heap = Runtime::Current()->GetHeap();
      const bool add_finalizer = kCheckAddFinalizer && IsFinalizable();
      if (!kCheckAddFinalizer) {
        DCHECK(!IsFinalizable());
      }
      mirror::Object* obj =
          heap->AllocObjectWithAllocator<kIsInstrumented, false>(self, this, this->object_size_,
                                                                 allocator_type, VoidFunctor());
      if (add_finalizer && LIKELY(obj != nullptr)) {
        heap->AddFinalizerReference(self, &obj);
        if (UNLIKELY(self->IsExceptionPending())) {
          // Failed to allocate finalizer reference, it means that the whole allocation failed.
          obj = nullptr;
        }
      }
      return obj;
    }
    

    在Java堆中创建对象时,如果发现class定义了finalize这个方法,那么就会新建一个FinalizerReference,指向这个新建的对象。

    void Heap::AddFinalizerReference(Thread* self, mirror::Object** object) {
      ScopedObjectAccess soa(self);
      ScopedLocalRef<jobject> arg(self->GetJniEnv(), soa.AddLocalReference<jobject>(*object));
      jvalue args[1];
      args[0].l = arg.get();
      InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_FinalizerReference_add, args);
      // Restore object in case it gets moved.
      *object = soa.Decode<mirror::Object*>(arg.get());
    }
    

    其中会调用Java中FinalizerReference的静态方法add, 方法中会创建一个FinalizerRefence对象,同时链入FinalizerReferencehead变量指向的静态链表中

        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;
            }
        }
    

    在对象被回收后,会调用ReferenceProcessorDelayReferenceReferent将FinalizerRefence放入finalizer_reference_queue_队列中

    void ReferenceProcessor::DelayReferenceReferent(mirror::Class* klass, mirror::Reference* ref,
                                                    collector::GarbageCollector* collector) {
      // klass can be the class of the old object if the visitor already updated the class of ref.
      DCHECK(klass != nullptr);
      DCHECK(klass->IsTypeOfReferenceClass());
      mirror::HeapReference<mirror::Object>* referent = ref->GetReferentReferenceAddr();
      if (referent->AsMirrorPtr() != nullptr && !collector->IsMarkedHeapReference(referent)) {
        Thread* self = Thread::Current();
        // TODO: Remove these locks, and use atomic stacks for storing references?
        // We need to check that the references haven't already been enqueued since we can end up
        // scanning the same reference multiple times due to dirty cards.
        if (klass->IsSoftReferenceClass()) {
          soft_reference_queue_.AtomicEnqueueIfNotEnqueued(self, ref);
        } else if (klass->IsWeakReferenceClass()) {
          weak_reference_queue_.AtomicEnqueueIfNotEnqueued(self, ref);
        } else if (klass->IsFinalizerReferenceClass()) {
          finalizer_reference_queue_.AtomicEnqueueIfNotEnqueued(self, ref);
        } else if (klass->IsPhantomReferenceClass()) {
          phantom_reference_queue_.AtomicEnqueueIfNotEnqueued(self, ref);
        } else {
          LOG(FATAL) << "Invalid reference type " << PrettyClass(klass) << " " << std::hex
                     << klass->GetAccessFlags();
        }
      }
    }
    

    ReferenceProcessorProcessReferences中会调用finalizer_reference_queue_EnqueueFinalizerReferences方法

    void ReferenceProcessor::ProcessReferences(bool concurrent, TimingLogger* timings,
                                               bool clear_soft_references,
                                               collector::GarbageCollector* collector) {
      TimingLogger::ScopedTiming t(concurrent ? __FUNCTION__ : "(Paused)ProcessReferences", timings);
      Thread* self = Thread::Current();
      {
        ....
        // Preserve all white objects with finalize methods and schedule them for finalization.
        finalizer_reference_queue_.EnqueueFinalizerReferences(&cleared_references_, collector);
        ......
      }
    

    EnqueueFinalizerReferences方法中会从队列中依次取出等待处理的FinalizerRefence,然后将FinalizerRefencereferent置为空,将指向的对象设置到zombiefield中,同时将FinalizerRefence放入cleared_references

    void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue* cleared_references,
                                                    collector::GarbageCollector* collector) {
      while (!IsEmpty()) {
        mirror::FinalizerReference* ref = DequeuePendingReference()->AsFinalizerReference();
        mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
        if (referent_addr->AsMirrorPtr() != nullptr &&
            !collector->IsMarkedHeapReference(referent_addr)) {
          mirror::Object* forward_address = collector->MarkObject(referent_addr->AsMirrorPtr());
          // Move the updated referent to the zombie field.
          if (Runtime::Current()->IsActiveTransaction()) {
            ref->SetZombie<true>(forward_address);
            ref->ClearReferent<true>();
          } else {
            ref->SetZombie<false>(forward_address);
            ref->ClearReferent<false>();
          }
          cleared_references->EnqueueReference(ref);
        }
      }
    }
    

    ClearedReferenceTask中会将cleared_references_中的reference通过Java中ReferenceQueue.add方法添加到Java ReferenceQueue的unenqueued队列中

    class ClearedReferenceTask : public HeapTask {
     public:
      explicit ClearedReferenceTask(jobject cleared_references)
          : HeapTask(NanoTime()), cleared_references_(cleared_references) {
      }
      virtual void Run(Thread* thread) {
        ScopedObjectAccess soa(thread);
        jvalue args[1];
        args[0].l = cleared_references_;
        InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_ReferenceQueue_add, args);
        soa.Env()->DeleteGlobalRef(cleared_references_);
      }
    
     private:
      const jobject cleared_references_;
    };
    

    ReferenceQueue.java

        public static Reference<?> unenqueued = null;
    
        static void add(Reference<?> list) {
            synchronized (ReferenceQueue.class) {
                if (unenqueued == null) {
                    unenqueued = list;
                } else {
                    // Find the last element in unenqueued.
                    Reference<?> last = unenqueued;
                    while (last.pendingNext != unenqueued) {
                      last = last.pendingNext;
                    }
                    // Add our list to the end. Update the pendingNext to point back to enqueued.
                    last.pendingNext = list;
                    last = list;
                    while (last.pendingNext != list) {
                        last = last.pendingNext;
                    }
                    last.pendingNext = unenqueued;
                }
                ReferenceQueue.class.notifyAll();
            }
        }
    

    虚拟机在Java层会启动三个daemon线程

    
    public final class Daemons {
    
        public static void start() {
            ReferenceQueueDaemon.INSTANCE.start(); //将unqueued链表中的Reference放入对用的ReferenceQueue中
            FinalizerDaemon.INSTANCE.start();  //将FinalizerRefenceQueue中的对象调用finalize方法
            FinalizerWatchdogDaemon.INSTANCE.start(); //防止finalize方法调用时间过长
            HeapTaskDaemon.INSTANCE.start();
        }
    }
    

    先看ReferenceQueueDaemonrun方法

        private static class ReferenceQueueDaemon extends Daemon {
            private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
    
            ReferenceQueueDaemon() {
                super("ReferenceQueueDaemon");
            }
    
            @Override public void run() {
                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;
                    }
                    ReferenceQueue.enqueuePending(list);
                }
            }
        }
    
    

    ReferenceQueue.unenqueued中的Reference调用ReferenceQueue.enqueuePending方法

    ReferenceQueue.java

        public static void enqueuePending(Reference<?> list) {
            Reference<?> start = list;
            do {
                ReferenceQueue queue = list.queue;
                if (queue == null) {
                    Reference<?> next = list.pendingNext;
    
                    // Make pendingNext a self-loop to preserve the invariant that
                    // once enqueued, pendingNext is non-null -- without leaking
                    // the object pendingNext was previously pointing to.
                    list.pendingNext = list;
                    list = next;
                } else {
                    // To improve performance, we try to avoid repeated
                    // synchronization on the same queue by batching enqueue of
                    // consecutive references in the list that have the same
                    // queue.
                    synchronized (queue.lock) {
                        do {
                            Reference<?> next = list.pendingNext;
    
                            // Make pendingNext a self-loop to preserve the
                            // invariant that once enqueued, pendingNext is
                            // non-null -- without leaking the object pendingNext
                            // was previously pointing to.
                            list.pendingNext = list;
                            queue.enqueueLocked(list);
                            list = next;
                        } while (list != start && list.queue == queue);
                        queue.lock.notifyAll();
                    }
                }
            } while (list != start);
        }
    

    将Reference放入对应的ReferenceQueue中去。所以对于SoftReference和WeakReference来说,进入ReferenceQueue就说明被回收了。LeakCanary就是通过WeakReferenceQueue来检测对象是否回收以判断是否内存泄漏的

    对于FinalizerRefence来说,还没有完

        private static class FinalizerDaemon extends Daemon {
            private static final FinalizerDaemon INSTANCE = new FinalizerDaemon();
            private final ReferenceQueue<Object> queue = FinalizerReference.queue;
            private final AtomicInteger progressCounter = new AtomicInteger(0);
            // Object (not reference!) being finalized. Accesses may race!
            private Object finalizingObject = null;
    
            FinalizerDaemon() {
                super("FinalizerDaemon");
            }
    
            @Override public void run() {
                // This loop may be performance critical, since we need to keep up with mutator
                // generation of finalizable objects.
                // We minimize the amount of work we do per finalizable object. For example, we avoid
                // reading the current time here, since that involves a kernel call per object.  We
                // limit fast path communication with FinalizerWatchDogDaemon to what's unavoidable: A
                // non-volatile store to communicate the current finalizable object, e.g. for
                // reporting, and a release store (lazySet) to a counter.
                // We do stop the  FinalizerWatchDogDaemon if we have nothing to do for a
                // potentially extended period.  This prevents the device from waking up regularly
                // during idle times.
    
                // Local copy of progressCounter; saves a fence per increment on ARM and MIPS.
                int localProgressCounter = progressCounter.get();
    
                while (isRunning()) {
                    try {
                        // Use non-blocking poll to avoid FinalizerWatchdogDaemon communication
                        // when busy.
                        FinalizerReference<?> finalizingReference = (FinalizerReference<?>)queue.poll();
                        if (finalizingReference != null) {
                            finalizingObject = finalizingReference.get();
                            progressCounter.lazySet(++localProgressCounter);
                        } else {
                            finalizingObject = null;
                            progressCounter.lazySet(++localProgressCounter);
                            // Slow path; block.
                            FinalizerWatchdogDaemon.INSTANCE.goToSleep();
                            finalizingReference = (FinalizerReference<?>)queue.remove();
                            finalizingObject = finalizingReference.get();
                            progressCounter.set(++localProgressCounter);
                            FinalizerWatchdogDaemon.INSTANCE.wakeUp();
                        }
                        doFinalize(finalizingReference);
                    } catch (InterruptedException ignored) {
                    } catch (OutOfMemoryError ignored) {
                    }
                }
            }
    
            @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
            private void doFinalize(FinalizerReference<?> reference) {
                FinalizerReference.remove(reference);
                Object object = reference.get();
                reference.clear();
                try {
                    object.finalize();
                } catch (Throwable ex) {
                    // The RI silently swallows these, but Android has always logged.
                    System.logE("Uncaught exception thrown by finalizer", ex);
                } finally {
                    // Done finalizing, stop holding the object as live.
                    finalizingObject = null;
                }
            }
        }
    

    FinalizerDaemon中,会循环检查FinalizerRefenceQueue中是否有Reference,如果有,就会取出,调用对象的finalize方法,同时将zombie置为null,使得对象可以被回收。

    所以FinalizerRefence很多,只能说明很多对象定义了finalize方法,而且还活在内存中,并不能表示产生了内存泄漏

    相关文章

      网友评论

        本文标题:FinalizerReference 笔记

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