做内存泄漏分析时,总是可以在HeapDump中看到这么一个类:FinalizerReference
数量很多,而且Retained Size还不小,那么他是内存泄漏的标志么?
template
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(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 arg(self->GetJniEnv(), soa.AddLocalReference(*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(arg.get());
}
其中会调用Java中FinalizerReference
的静态方法add
, 方法中会创建一个FinalizerRefence
对象,同时链入FinalizerReference
的head
变量指向的静态链表中
public static void add(Object referent) {
FinalizerReference> reference = new FinalizerReference
在对象被回收后,会调用ReferenceProcessor
的DelayReferenceReferent
将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* 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();
}
}
}
在ReferenceProcessor
的ProcessReferences
中会调用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
,然后将FinalizerRefence
的referent
置为空,将指向的对象设置到zombie
field中,同时将FinalizerRefence放入cleared_references
中
void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue* cleared_references,
collector::GarbageCollector* collector) {
while (!IsEmpty()) {
mirror::FinalizerReference* ref = DequeuePendingReference()->AsFinalizerReference();
mirror::HeapReference* 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(forward_address);
ref->ClearReferent();
} else {
ref->SetZombie(forward_address);
ref->ClearReferent();
}
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();
}
}
先看ReferenceQueueDaemon
的run
方法
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
在FinalizerDaemon
中,会循环检查FinalizerRefenceQueue
中是否有Reference
,如果有,就会取出,调用对象的finalize
方法,同时将zombie置为null,使得对象可以被回收。
所以FinalizerRefence很多,只能说明很多对象定义了finalize方法,而且还活在内存中,并不能表示产生了内存泄漏