Dalvik虚拟机 Finalize 方法执行分析

Finalize 方法是java object在被heap完全回收之前,一定会被调用的方法。但是,虚拟机规范不保证该方法会在什么时候执行。前段时间,有个在native out of memory的bug,就是因为android的cts中有个leakmemory测试,会大量分分配native内存,然后在finalize中释放。但是由于lemur并没有及时的保证执行finalize方法导致虚拟地址用完程序crash。 所以建议,劲量不要依赖finalize方法去释放紧缺的资源。

一. Finalize方法

在虚拟机内部,对于有Finalize方法的object,其实是通过FinalizeReference来实现的。FinalizeReference类似与softReference,weakReference等,都派生自Reference类。

不同的是,softReference这些是在java代码中显式的new 出来的,比如:

  1. private static final WeakReference<String> weakTest = new WeakReference<String>(new String("weak"));
    private static final WeakReference<String> weakTest =  new WeakReference<String>(new String("weak")); 
但是,FinalizeReference不同,我们是不会显示的去new 一个Finalize reference的,换句话将对java层的程序员是透明的。

那么虚拟机是怎么处理的呢,是什么时候去new出Finalize reference的呢,并且怎样去执行finalize方法的呢?

这一切可以从虚拟机的class loader机制说起。虚拟机对class的load往细了说比较复杂,需要以后开专题讨论,但是概括的讲,就是几个过程:

FindClass->DefineClass->LoadClassFromDex->LoadMethod

在LoadMethod的阶段,虚拟机会去判断,该class的函数表中是否含有Finalize方法,如果存在Finalize方法就会给该class置一个标记位,对应与dalvik的代码 dalvik/vm/oo/class.cpp LoadMethodFromDex函数:

  1. static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,
  2. Method* meth)
  3. {
  4. DexFile* pDexFile = clazz->pDvmDex->pDexFile;
  5. const DexMethodId* pMethodId;
  6. const DexCode* pDexCode;
  7. pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
  8. meth->name = dexStringById(pDexFile, pMethodId->nameIdx);
  9. dexProtoSetFromMethodId(&meth->prototype, pDexFile, pMethodId);
  10. meth->shorty = dexProtoGetShorty(&meth->prototype);
  11. meth->accessFlags = pDexMethod->accessFlags;
  12. meth->clazz = clazz;
  13. meth->jniArgInfo = 0;
  14. <SPAN style="COLOR: #ff0000"> if (dvmCompareNameDescriptorAndMethod("finalize", "()V", meth) == 0) {
  15. /*
  16. * The Enum class declares a "final" finalize() method to
  17. * prevent subclasses from introducing a finalizer. We don't
  18. * want to set the finalizable flag for Enum or its subclasses,
  19. * so we check for it here.
  20. *
  21. * We also want to avoid setting it on Object, but it's easier
  22. * to just strip that out later.
  23. */
  24. if (clazz->classLoader != NULL ||
  25. strcmp(clazz->descriptor, "Ljava/lang/Enum;") != 0)
  26. {
  27. SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
  28. }
  29. }</SPAN>
static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,
    Method* meth)
{
    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
    const DexMethodId* pMethodId;
    const DexCode* pDexCode;

    pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);

    meth->name = dexStringById(pDexFile, pMethodId->nameIdx);
    dexProtoSetFromMethodId(&meth->prototype, pDexFile, pMethodId);
    meth->shorty = dexProtoGetShorty(&meth->prototype);
    meth->accessFlags = pDexMethod->accessFlags;
    meth->clazz = clazz;
    meth->jniArgInfo = 0;

<span style="color:#ff0000;">    if (dvmCompareNameDescriptorAndMethod("finalize", "()V", meth) == 0) {
        /*
         * The Enum class declares a "final" finalize() method to
         * prevent subclasses from introducing a finalizer.  We don't
         * want to set the finalizable flag for Enum or its subclasses,
         * so we check for it here.
         *
         * We also want to avoid setting it on Object, but it's easier
         * to just strip that out later.
         */
        if (clazz->classLoader != NULL ||
            strcmp(clazz->descriptor, "Ljava/lang/Enum;") != 0)
        {
            SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
        }
    }</span>
这里有个问题,因为java/lang/object是含有finalize方法的,而其他的object包括class object都是继承自java/lang/object的,按理所,
  1. <SPAN style="COLOR: #ff0000"> dvmCompareNameDescriptorAndMethod("finalize", "()V", meth)
  2. </SPAN>
<span style="color:#ff0000;">          dvmCompareNameDescriptorAndMethod("finalize", "()V", meth)
</span>

这个判断会永久成立,但是请注意,在dalvik中,loadMethodFromdex是在LinkClass之前做的,即这个时候,父类的method还没有被拷贝到当前load的class的函数表中。所以不存在以上问题。

而在我们的lemur虚拟机中,对于Finalize方法的判断是在LinkClass以后,所以需要特别注意这个问题。

完成标记以后,会需要在object从heap分配以后为该object执行add finalize reference操作,具体的时间点,可以根据虚拟机的实现选择,这里dalvik的实现和lemur的实现略有不同:

1. dalvik在执行java/lang/obect的init方法之前,代码在汇编解释器中, 代码在vm/mterp/out/InterpAsm-armv7-a.S

  1. .L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
  2. /* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
  3. /*
  4. * Invoke Object.<init> on an object. In practice we know that
  5. * Object's nullary constructor doesn't do anything, so we just
  6. * skip it unless a debugger is active.
  7. */
  8. FETCH(r1, 2) @ r1<- CCCC
  9. GET_VREG(r0, r1) @ r0<- "this" ptr
  10. cmp r0, #0 @ check for NULL
  11. beq common_errNullObject @ export PC and throw NPE
  12. ldr r1, [r0, #offObject_clazz] @ r1<- obj->clazz
  13. ldr r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
  14. tst r2, #CLASS_ISFINALIZABLE @ is this class finalizable?
  15. bne .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal @ yes, go
.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
/* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
    /*
     * Invoke Object.<init> on an object.  In practice we know that
     * Object's nullary constructor doesn't do anything, so we just
     * skip it unless a debugger is active.
     */
    FETCH(r1, 2)                  @ r1<- CCCC
    GET_VREG(r0, r1)                    @ r0<- "this" ptr
    cmp     r0, #0                      @ check for NULL
    beq     common_errNullObject        @ export PC and throw NPE
    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal        @ yes, go
  1. .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
  2. EXPORT_PC() @ can throw
  3. bl dvmSetFinalizable @ call dvmSetFinalizable(obj)
  4. ldr r0, [rSELF, #offThread_exception] @ r0<- self->exception
  5. cmp r0, #0 @ exception pending?
  6. bne common_exceptionThrown @ yes, handle it
  7. b .LOP_INVOKE_OBJECT_INIT_RANGE_finish
.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
    EXPORT_PC()                         @ can throw
    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
    cmp     r0, #0                      @ exception pending?
    bne     common_exceptionThrown      @ yes, handle it
    b       .LOP_INVOKE_OBJECT_INIT_RANGE_finish

在dvmSetFinalizeable中,虚拟机会回调java层的libcore中FinalizeRerence中的add函数
  1. void dvmSetFinalizable(Object *obj)
  2. {
  3. assert(obj != NULL);
  4. Thread *self = dvmThreadSelf();
  5. assert(self != NULL);
  6. Method *meth = gDvm.methJavaLangRefFinalizerReferenceAdd;
  7. assert(meth != NULL);
  8. JValue unusedResult;
  9. dvmCallMethod(self, meth, NULL, &unusedResult, obj);
  10. }
void dvmSetFinalizable(Object *obj)
{
    assert(obj != NULL);
    Thread *self = dvmThreadSelf();
    assert(self != NULL);
    Method *meth = gDvm.methJavaLangRefFinalizerReferenceAdd;
    assert(meth != NULL);
    JValue unusedResult;
    dvmCallMethod(self, meth, NULL, &unusedResult, obj);
}

FinalizeReference中add函数的实现在libcore/luni/src/main/java/java/lang/ref/Finalizereference.java中:

  1. public static void add(Object referent) {
  2. <SPAN style="COLOR: #ff0000">FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue);</SPAN>
  3. synchronized (LIST_LOCK) {
  4. reference.prev = null;
  5. reference.next = head;
  6. if (head != null) {
  7. head.prev = reference;
  8. }
  9. head = reference;
  10. }
  11. }
   public static void add(Object referent) {
        <span style="color:#ff0000;">FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue);</span>
        synchronized (LIST_LOCK) {
            reference.prev = null;
            reference.next = head;
            if (head != null) {
                head.prev = reference;
            }
            head = reference;
        }
    }

在这里,终于看到在libcore中,我们new 出来了一个FinalizeReference,并且把含有Finalize的方法放到了新new出来的FinalizeReferce的referent域(具体实现可以去参看FinalizeReference的构造函数)。然后,以链表的形式串起来,head是FinalizeReferenc中的一个static域,

  1. private static FinalizerReference<?> head = null;
 private static FinalizerReference<?> head = null;

用heap头引用住,可以确保Finalize reference可以在heap gc的mark阶段被mark到。


二 Finalize的 执行

上面我们详细的分析了一个含有finalize方法的object转化为一个Finalize reference的过程,下面我们分析,在Heap gc掉该object以后,finalize进行了那些操作,finalize方法又是怎么执行的。

Dalvik的garbage gc的算法使用的是CMS(Concurrent mark-sweep )的算法,即先通过root集mark所以live的object,然后sweep掉所有非mark过的object。Gc也是虚拟机里面很重要的一个模块,我们以后再讨论,在这里只是粗略的总结一下,以可以继续我们后面的讨论。典型的concurrent mark-sweep算法分为5个阶段:

1. initliaze phase

2. mark root

3. scan object

4. remark

5. reclaim

处理reference的主要在scan object, remark和reclaim阶段。

在scan object阶段,我们需要区分出所有的refence并且用链表把所有的相应的reference串起来,请注意这里串起来的是FinalizeReference,而不是含有finalize方法的object,但是object在该FinalizeReference的referent域中。具体的代码在vm/alloc/marksweep.cpp中:

  1. static void scanDataObject(const Object *obj, GcMarkContext *ctx)
  2. {
  3. assert(obj != NULL);
  4. assert(obj->clazz != NULL);
  5. assert(ctx != NULL);
  6. markObject((const Object *)obj->clazz, ctx);
  7. scanFields(obj, ctx);
  8. if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
  9. delayReferenceReferent((Object *)obj, ctx);
  10. }
  11. }
static void scanDataObject(const Object *obj, GcMarkContext *ctx)
{
    assert(obj != NULL);
    assert(obj->clazz != NULL);
    assert(ctx != NULL);
    markObject((const Object *)obj->clazz, ctx);
    scanFields(obj, ctx);
    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
        delayReferenceReferent((Object *)obj, ctx);
    }
}


所有的reference都会进入delayReferencereferent中处理:

  1. /*
  2. * Process the "referent" field in a java.lang.ref.Reference. If the
  3. * referent has not yet been marked, put it on the appropriate list in
  4. * the gcHeap for later processing.
  5. */
  6. static void delayReferenceReferent(Object *obj, GcMarkContext *ctx)
  7. {
  8. assert(obj != NULL);
  9. assert(obj->clazz != NULL);
  10. assert(IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE));
  11. assert(ctx != NULL);
  12. GcHeap *gcHeap = gDvm.gcHeap;
  13. size_t pendingNextOffset = gDvm.offJavaLangRefReference_pendingNext;
  14. size_t referentOffset = gDvm.offJavaLangRefReference_referent;
  15. Object *pending = dvmGetFieldObject(obj, pendingNextOffset);
  16. Object *referent = dvmGetFieldObject(obj, referentOffset);
  17. if (pending == NULL && referent != NULL && !isMarked(referent, ctx)) {
  18. Object **list = NULL;
  19. if (isSoftReference(obj)) {
  20. list = &gcHeap->softReferences;
  21. } else if (isWeakReference(obj)) {
  22. list = &gcHeap->weakReferences;
  23. <SPAN style="COLOR: #ff0000"> } else if (isFinalizerReference(obj)) {
  24. list = &gcHeap->finalizerReferences;</SPAN>
  25. } else if (isPhantomReference(obj)) {
  26. list = &gcHeap->phantomReferences;
  27. }
  28. assert(list != NULL);
  29. <SPAN style="COLOR: #ff6666"> enqueuePendingReference(obj, list);</SPAN>
  30. }
  31. }
/*
 * 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)
{
    assert(obj != NULL);
    assert(obj->clazz != NULL);
    assert(IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE));
    assert(ctx != NULL);
    GcHeap *gcHeap = gDvm.gcHeap;
    size_t pendingNextOffset = gDvm.offJavaLangRefReference_pendingNext;
    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
    Object *pending = dvmGetFieldObject(obj, pendingNextOffset);
    Object *referent = dvmGetFieldObject(obj, referentOffset);
    if (pending == NULL && referent != NULL && !isMarked(referent, ctx)) {
        Object **list = NULL;
        if (isSoftReference(obj)) {
            list = &gcHeap->softReferences;
        } else if (isWeakReference(obj)) {
            list = &gcHeap->weakReferences;
<span style="color:#ff0000;">        } else if (isFinalizerReference(obj)) {
            list = &gcHeap->finalizerReferences;</span>
        } else if (isPhantomReference(obj)) {
            list = &gcHeap->phantomReferences;
        }
        assert(list != NULL);
       <span style="color:#ff6666;"> enqueuePendingReference(obj, list);</span>
    }
}

至于isfinalizerReferenc(obj)的实现就很简单,只需要判断obj的clazz是否是java/lang/FinalizeReference即可。

enqueuPendingReference就是一个串链表的过程,这里就不贴代码了。

完成mark阶段以后,所有的reference都已经被被串在了一起,在remark完以后会真正的处理FinalizeReference中的object,把object取出来,mark 它然后把它从FinalizeReference中的引用中剥离出来,代码在: vm/alloc/marksweep.cpp中:

  1. static void enqueueFinalizerReferences(Object **list)
  2. {
  3. assert(list != NULL);
  4. GcMarkContext *ctx = &gDvm.gcHeap->markContext;
  5. size_t referentOffset = gDvm.offJavaLangRefReference_referent;
  6. size_t zombieOffset = gDvm.offJavaLangRefFinalizerReference_zombie;
  7. bool hasEnqueued = false;
  8. while (*list != NULL) {
  9. <SPAN style="COLOR: #ff0000"> Object *ref = dequeuePendingReference(list);
  10. Object *referent = dvmGetFieldObject(ref, referentOffset);
  11. if (referent != NULL && !isMarked(referent, ctx)) {
  12. markObject(referent, ctx);
  13. /* If the referent is non-null the reference must queuable. */
  14. assert(isEnqueuable(ref));
  15. dvmSetFieldObject(ref, zombieOffset, referent);
  16. clearReference(ref);
  17. enqueueReference(ref);
  18. hasEnqueued = true;
  19. }</SPAN>
  20. }
  21. if (hasEnqueued) {
  22. processMarkStack(ctx);
  23. }
  24. assert(*list == NULL);
  25. }
static void enqueueFinalizerReferences(Object **list)
{
    assert(list != NULL);
    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
    size_t zombieOffset = gDvm.offJavaLangRefFinalizerReference_zombie;
    bool hasEnqueued = false;
    while (*list != NULL) {
<span style="color:#ff0000;">        Object *ref = dequeuePendingReference(list);
        Object *referent = dvmGetFieldObject(ref, referentOffset);
        if (referent != NULL && !isMarked(referent, ctx)) {
            markObject(referent, ctx);
            /* If the referent is non-null the reference must queuable. */
            assert(isEnqueuable(ref));
            dvmSetFieldObject(ref, zombieOffset, referent);
            clearReference(ref);
            enqueueReference(ref);
            hasEnqueued = true;
        }</span>
    }
    if (hasEnqueued) {
        processMarkStack(ctx);
    }
    assert(*list == NULL);
}

有没有看到,红色的部分,正是从该FinalizeReference中取出了object,然后把referent从Referenct的referent域移到zombie域中,这两个域对对应于libcore FinalizeReference中的两个域。完了以后,还需要把该reference连到cleardReferences链表中:

  1. /*
  2. * Schedules a reference to be appended to its reference queue.
  3. */
  4. static void enqueueReference(Object *ref)
  5. {
  6. assert(ref != NULL);
  7. assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
  8. assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
  9. <SPAN style="COLOR: #ff0000"> enqueuePendingReference(ref, &gDvm.gcHeap->clearedReferences);</SPAN>
  10. }
/*
 * Schedules a reference to be appended to its reference queue.
 */
static void enqueueReference(Object *ref)
{
    assert(ref != NULL);
    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
<span style="color:#ff0000;">    enqueuePendingReference(ref, &gDvm.gcHeap->clearedReferences);</span>
}

这个cleardReference是干嘛用的呢? 答案看下面的函数:

  1. void dvmEnqueueClearedReferences(Object **cleared)
  2. {
  3. assert(cleared != NULL);
  4. if (*cleared != NULL) {
  5. Thread *self = dvmThreadSelf();
  6. assert(self != NULL);
  7. <SPAN style="COLOR: #ff0000"> Method *meth = gDvm.methJavaLangRefReferenceQueueAdd;</SPAN>
  8. assert(meth != NULL);
  9. JValue unused;
  10. Object *reference = *cleared;
  11. <SPAN style="COLOR: #ff0000">dvmCallMethod(self, meth, NULL, &unused, reference);</SPAN>
  12. *cleared = NULL;
  13. }
  14. }
void dvmEnqueueClearedReferences(Object **cleared)
{
    assert(cleared != NULL);
    if (*cleared != NULL) {
        Thread *self = dvmThreadSelf();
        assert(self != NULL);
       <span style="color:#ff0000;"> Method *meth = gDvm.methJavaLangRefReferenceQueueAdd;</span>
        assert(meth != NULL);
        JValue unused;
        Object *reference = *cleared;
        <span style="color:#ff0000;">dvmCallMethod(self, meth, NULL, &unused, reference);</span>
        *cleared = NULL;
    }
}  
是的,它正是用来将所有需要释放的Refernce返回到java 层用的。

至此,虚拟机内部对Finalize的处理已经结束,你是不是会很奇怪,因为搞了半天,貌似并没有地方真正地执行了object的finalize方法。

是的,真正的Finalize方法不是由虚拟机执行的,虚拟机只是把reference链接到需要处理的表中,真正的执行由Finalize Daemon进程来处理。 Finalize Daemon是一个守护进程,在那里监控Finalize Reference的queue中是否有新的reference进来,如果是,就去执行该object的finalize方法,代码在 libcore/libdvm/src/main/java/java/lang/Daemons.jav中:

  1. @Override public void run() {
  2. while (isRunning()) {
  3. // Take a reference, blocking until one is ready or the thread should stop
  4. try {
  5. <SPAN style="COLOR: #ff0000">doFinalize((FinalizerReference<?>) queue.remove());</SPAN>
  6. } catch (InterruptedException ignored) {
  7. }
  8. }
  9. }
  10. @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
  11. private void doFinalize(FinalizerReference<?> reference) {
  12. SPAN style="COLOR: #ff0000"> FinalizerReference.remove(reference);
  13. Object object = reference.get();</SPAN>
  14. reference.clear();
  15. try {
  16. finalizingStartedNanos = System.nanoTime();
  17. finalizingObject = object;
  18. synchronized (FinalizerWatchdogDaemon.INSTANCE) {
  19. FinalizerWatchdogDaemon.INSTANCE.notify();
  20. }
  21. <SPAN style="COLOR: #ff0000"> object.finalize();</SPAN>
  22. } catch (Throwable ex) {
  23. // The RI silently swallows these, but Android has always logged.
  24. System.logE("Uncaught exception thrown by finalizer", ex);
  25. } finally {
  26. finalizingObject = null;
  27. }
  28. }
  29. }
        @Override public void run() {
            while (isRunning()) {
                // Take a reference, blocking until one is ready or the thread should stop
                try {
                    <span style="color:#ff0000;">doFinalize((FinalizerReference<?>) queue.remove());</span>
                } catch (InterruptedException ignored) {
                }
            }
        }

        @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
        private void doFinalize(FinalizerReference<?> reference) {
   <span style="color:#ff0000;">         FinalizerReference.remove(reference);
            Object object = reference.get();</span>
            reference.clear();
            try {
                finalizingStartedNanos = System.nanoTime();
                finalizingObject = object;
                synchronized (FinalizerWatchdogDaemon.INSTANCE) {
                    FinalizerWatchdogDaemon.INSTANCE.notify();
                }
         <span style="color:#ff0000;">       object.finalize();</span>
            } catch (Throwable ex) {
                // The RI silently swallows these, but Android has always logged.
                System.logE("Uncaught exception thrown by finalizer", ex);
            } finally {
                finalizingObject = null;
            }
        }
    }
但是又是谁来把reference放到Finalize Reference的queue中的呢,我从libcore中看到了两条路径,一条是从上面的cleard refernce queue中,它会被另一个daemon, ReferenceQueueDaemon处理,我们来看看它的处理过程:

  1. @Override public void run() {
  2. while (isRunning()) {
  3. Reference<?> list;
  4. try {
  5. synchronized (ReferenceQueue.class) {
  6. while (ReferenceQueue.unenqueued == null) {
  7. ReferenceQueue.class.wait();
  8. }
  9. list = ReferenceQueue.unenqueued;
  10. ReferenceQueue.unenqueued = null;
  11. }
  12. } catch (InterruptedException e) {
  13. continue;
  14. }
  15. enqueue(list);
  16. }
  17. }
  18. private void enqueue(Reference<?> list) {
  19. while (list != null) {
  20. Reference<?> reference;
  21. // pendingNext is owned by the GC so no synchronization is required
  22. if (list == list.pendingNext) {
  23. reference = list;
  24. reference.pendingNext = null;
  25. list = null;
  26. } else {
  27. reference = list.pendingNext;
  28. list.pendingNext = reference.pendingNext;
  29. reference.pendingNext = null;
  30. }
  31. <SPAN style="COLOR: #ff0000">reference.enqueueInternal();</SPAN>
  32. }
  33. }
  34. }
    @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;
                }
                enqueue(list);
            }
        }

        private void enqueue(Reference<?> list) {
            while (list != null) {
                Reference<?> reference;
                // pendingNext is owned by the GC so no synchronization is required
                if (list == list.pendingNext) {
                    reference = list;
                    reference.pendingNext = null;
                    list = null;
                } else {
                    reference = list.pendingNext;
                    list.pendingNext = reference.pendingNext;
                    reference.pendingNext = null;
                }
                <span style="color:#ff0000;">reference.enqueueInternal();</span>
            }
        }
    }

看最后的enqueuIntenal函数,该函数的在Reference类里面:

  1. public final synchronized boolean enqueueInternal() {
  2. if (queue != null && queueNext == null) {
  3. <SPAN style="COLOR: #ff0000"> queue.enqueue(this);</SPAN>
  4. queue = null;
  5. return true;
  6. }
  7. return false;
  8. }
    public final synchronized boolean enqueueInternal() {
        if (queue != null && queueNext == null) {
           <span style="color:#ff0000;"> queue.enqueue(this);</span>
            queue = null;
            return true;
        }
        return false;
    }

对了,正是在该点,cleared queue中的Finalize Reference被转移到了Finalize 类中的queue中,然后被Finalize Daemon守护线程执行。


三 总结

我们从虚拟机和libcore的角度分析了Finalize方法的执行流程,到现在我们可以得出以下两点结论:

1. Finalize 方法被执行的时间不确定,所以不能依赖与它来释放紧缺的资源。时间不确定的原因是:

a. 虚拟机调用GC的时间不确定

b. Finalize daemon线程被调度到的时间不确定

2. Finalize方法只会被执行一次,即使对象被复活,如果已经执行过了Finalize方法,再次被gc时也不会再执行了,原因是:

含有Finalize方法的object是在new的时候由虚拟机生成了一个Finalize reference在来引用到该Object的,而在Finalize方法执行的时候,该object所对应的Finalize Reference会被释放掉,即使在这个时候把该object复活(即用强引用引用住该object),再第二次被gc的时候由于没有了Finalize reference与之对应,所以Finalize方法不会再执行

3. 含有Finalize方法的object需要至少经过两轮GC才有可能被释放(所以在对内存回收速度有要求的情况下,)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值