binder native内存同步回收

 我们都知道binder机制主要逻辑都实现在natvie层,当client端通过servicemanager或者binder调用获取到binder bp句柄的时候会交给binderproxy持有,而binder bp句柄实体在native层

当binderproxy被内存回收的时候,持有的binder client句柄是怎么被回收的呢?

1:初始化

当通过binder bp句柄初始化binder proxy的时候会通过注册native 回收函数,来关联java对象与native对象,当java对象被回收的时候会同步回收native对象

    private static BinderProxy getInstance(long nativeData, long iBinder) {
        BinderProxy result;
        synchronized (sProxyMap) {
            try {
                result = sProxyMap.get(iBinder);
                if (result != null) {
                    return result;
                }
                result = new BinderProxy(nativeData);
            } catch (Throwable e) {
                // We're throwing an exception (probably OOME); don't drop nativeData.
                NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
                        nativeData);
                throw e;
            }
            NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);// 注册回收逻辑
            // The registry now owns nativeData, even if registration threw an exception.
            sProxyMap.set(iBinder, result);
        }
        return result;
    }

   private static class NoImagePreloadHolder {
        public static final long sNativeFinalizer = getNativeFinalizer();
        public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
                BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);  //sNativeFinalizer 这里的sNativeFinalizer即是binder client native层的回收逻辑
    }


JNIEXPORT jlong JNICALL android_os_Binder_getNativeFinalizer(JNIEnv*, jclass) {
    return (jlong) Binder_destroy;
}

static void Binder_destroy(void* rawJbh)
{
    JavaBBinderHolder* jbh = (JavaBBinderHolder*) rawJbh;
    ALOGV("Java Binder: deleting holder %p", jbh);
    delete jbh;
}

 2:注册

cleaner类是一个幽灵引用的子类,幽灵引用不会影响内存回收,也就是下次gc的时候会回收cleaner对象,jvm建议使用cleaner来代替finalize方法

    public @NonNull Runnable registerNativeAllocation(@NonNull Object referent, long nativePtr) {
        if (referent == null) {
            throw new IllegalArgumentException("referent is null");
        }
        if (nativePtr == 0) {
            throw new IllegalArgumentException("nativePtr is null");
        }

        CleanerThunk thunk;
        CleanerRunner result;
        try {
            thunk = new CleanerThunk();
            Cleaner cleaner = Cleaner.create(referent, thunk);
            result = new CleanerRunner(cleaner);
            registerNativeAllocation(this.size);
        } catch (VirtualMachineError vme /* probably OutOfMemoryError */) {
            applyFreeFunction(freeFunction, nativePtr);
            throw vme;
        } // Other exceptions are impossible.
        // Enable the cleaner only after we can no longer throw anything, including OOME.
        thunk.setNativePtr(nativePtr);
        // Ensure that cleaner doesn't get invoked before we enable it.
        Reference.reachabilityFence(referent);
        COUNTER.getAndAdd(this, 1);
        return result;
    }

    public static Cleaner create(Object ob, Runnable thunk) {
        if (thunk == null)
            return null;
        return add(new Cleaner(ob, thunk));
    }

/**
 * General-purpose phantom-reference-based cleaners.
 *
 * <p> Cleaners are a lightweight and more robust alternative to finalization.
 * They are lightweight because they are not created by the VM and thus do not
 * require a JNI upcall to be created, and because their cleanup code is
 * invoked directly by the reference-handler thread rather than by the
 * finalizer thread.  They are more robust because they use phantom references,
 * the weakest type of reference object, thereby avoiding the nasty ordering
 * problems inherent to finalization.
 *
 * <p> A cleaner tracks a referent object and encapsulates a thunk of arbitrary
 * cleanup code.  Some time after the GC detects that a cleaner's referent has
 * become phantom-reachable, the reference-handler thread will run the cleaner.
 * Cleaners may also be invoked directly; they are thread safe and ensure that
 * they run their thunks at most once.
 *
 * <p> Cleaners are not a replacement for finalization.  They should be used
 * only when the cleanup code is extremely simple and straightforward.
 * Nontrivial cleaners are inadvisable since they risk blocking the
 * reference-handler thread and delaying further cleanup and finalization.
 *
 *
 * @author Mark Reinhold
 */
public class Cleaner
    extends PhantomReference<Object>

3:收集

    通过扫描gcroot收集待回收的引用到referenceProcessor中的 cleared_references_变量中,其中就包括binder回收逻辑注册到的cleaner对象,最后通过referenceprocessor调用ReferenceQueue类的add方法,添加到回收链表中


collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type,
{
    ...
    collector->Run(gc_cause, clear_soft_references || runtime->IsZygote());//
    通过扫描gcroot收集待回收的软饮用到referenceProcessor中的 cleared_references_变量中,其中就包括binder注册到的cleaner
    IncrementFreedEver();
    RequestTrim(self);
    // Collect cleared references.
    clear = reference_processor_->CollectClearedReferences(self);
    //
    // Grow the heap so that we know when to perform the next GC.
    GrowForUtilization(collector, bytes_allocated_before_gc);
  }

class ClearedReferenceTask : public HeapTask {
 public:
  explicit ClearedReferenceTask(jobject cleared_references)
      : HeapTask(NanoTime()), cleared_references_(cleared_references) {
  }
  void Run(Thread* thread) override {
    ScopedObjectAccess soa(thread);
    WellKnownClasses::java_lang_ref_ReferenceQueue_add->InvokeStatic<'V', 'L'>(
        thread, soa.Decode<mirror::Object>(cleared_references_));
    soa.Env()->DeleteGlobalRef(cleared_references_);
  }

 private:
  const jobject cleared_references_;
};


SelfDeletingTask* ReferenceProcessor::CollectClearedReferences(Thread* self) {
  Locks::mutator_lock_->AssertNotHeld(self);
  // By default we don't actually need to do anything. Just return this no-op task to avoid having
  // to put in ifs.
  std::unique_ptr<SelfDeletingTask> result(new FunctionTask([](Thread*) {}));
  // When a runtime isn't started there are no reference queues to care about so ignore.
  if (!cleared_references_.IsEmpty()) {
    if (LIKELY(Runtime::Current()->IsStarted())) {
      jobject cleared_references;
      {
        ReaderMutexLock mu(self, *Locks::mutator_lock_);
        cleared_references = self->GetJniEnv()->GetVm()->AddGlobalRef(
            self, cleared_references_.GetList());
      }
      if (kAsyncReferenceQueueAdd) {
        // TODO: This can cause RunFinalization to terminate before newly freed objects are
        // finalized since they may not be enqueued by the time RunFinalization starts.
        Runtime::Current()->GetHeap()->GetTaskProcessor()->AddTask(
            self, new ClearedReferenceTask(cleared_references));
      } else {
        result.reset(new ClearedReferenceTask(cleared_references));
      }
    }
    cleared_references_.Clear();
  }
  return result.release();
}


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

4: 回收

回收是在一个单独的线程ReferenceQueueDaemon, 通过不断扫描链表是否被gc线程添加进新元素来,运行因为对象被回收而注册的cleaner逻辑(enqueuePending方法中的cleaner.clean)

    private static class ReferenceQueueDaemon extends Daemon {
        @UnsupportedAppUsage
        private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();

        // Monitored by FinalizerWatchdogDaemon to make sure we're still working.
        private final AtomicInteger progressCounter = new AtomicInteger(0);

        ReferenceQueueDaemon() {
            super("ReferenceQueueDaemon");
        }

        private long lastGcCount = 0;

        private void onRefQueueEmptyAfterGc() {
            long gcCount = VMRuntime.getFullGcCount();
            if (gcCount > lastGcCount) {
                VMRuntime.onPostCleanup();
                lastGcCount = gcCount;
            }
        }

        @Override public void runInternal() {
            FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(FinalizerWatchdogDaemon.RQ_DAEMON);

            // Call once early to reduce later allocation, and hence chance of OOMEs.
            FinalizerWatchdogDaemon.INSTANCE.resetTimeouts();

            lastGcCount = VMRuntime.getFullGcCount();

            while (isRunning()) {
                Reference<?> list;
                try {
                    synchronized (ReferenceQueue.class) {
                        if (ReferenceQueue.unenqueued == null) {
                            onRefQueueEmptyAfterGc();
                            FinalizerWatchdogDaemon.INSTANCE.monitoringNotNeeded(
                                    FinalizerWatchdogDaemon.RQ_DAEMON);
                            // Increment after above call. If watchdog saw it active, it should see
                            // the counter update.
                            progressCounter.incrementAndGet();
                            do {
                               ReferenceQueue.class.wait();
                            } while (ReferenceQueue.unenqueued == null);
                            progressCounter.incrementAndGet();
                            FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(
                                    FinalizerWatchdogDaemon.RQ_DAEMON);
                        }
                        list = ReferenceQueue.unenqueued;
                        ReferenceQueue.unenqueued = null;
                    }
                    ReferenceQueue.enqueuePending(list, progressCounter);
                    FinalizerWatchdogDaemon.INSTANCE.resetTimeouts();
                } catch (InterruptedException e) {
                    // Happens when we are asked to stop.
                } catch (OutOfMemoryError ignored) {
                    // Very unlikely. Cleaner.clean OOMEs are caught elsewhere, and nothing else
                    // should allocate regularly. Could result in enqueuePending dropping
                    // references. Does occur in tests that run out of memory.
                    System.logW(RQD_OOM_MESSAGE);
                }
            }
        }


    public static void enqueuePending(Reference<?> list, AtomicInteger progressCounter) {
        Reference<?> start = list;
        do {
            ReferenceQueue queue = list.queue;
            if (queue == null || sun.misc.Cleaner.isCleanerQueue(queue)) {
                Reference<?> next = list.pendingNext;
                // Always 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;
                if (queue != null) {
                    // This is a Cleaner. Run directly without additional synchronization.
                    sun.misc.Cleaner cl = (sun.misc.Cleaner) list;
                    currentTarget = cl;
                    cl.clean();  // Idempotent. No need to check queueNext first. Handles all
                                 // exceptions.
                    list.queueNext = sQueueNextUnenqueued;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值