我们都知道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;
}