115 lines
4.5 KiB
Java
115 lines
4.5 KiB
Java
package com.squareup.leakcanary;
|
|
|
|
import com.squareup.leakcanary.HeapDump;
|
|
import com.squareup.leakcanary.Retryable;
|
|
import java.io.File;
|
|
import java.lang.ref.ReferenceQueue;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.CopyOnWriteArraySet;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/* loaded from: classes.dex */
|
|
public final class RefWatcher {
|
|
public static final RefWatcher DISABLED = new RefWatcherBuilder().build();
|
|
private final DebuggerControl debuggerControl;
|
|
private final GcTrigger gcTrigger;
|
|
private final HeapDump.Builder heapDumpBuilder;
|
|
private final HeapDumper heapDumper;
|
|
private final HeapDump.Listener heapdumpListener;
|
|
private final WatchExecutor watchExecutor;
|
|
private final Set<String> retainedKeys = new CopyOnWriteArraySet();
|
|
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
|
|
|
|
RefWatcher(WatchExecutor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger, HeapDumper heapDumper, HeapDump.Listener listener, HeapDump.Builder builder) {
|
|
this.watchExecutor = (WatchExecutor) Preconditions.checkNotNull(watchExecutor, "watchExecutor");
|
|
this.debuggerControl = (DebuggerControl) Preconditions.checkNotNull(debuggerControl, "debuggerControl");
|
|
this.gcTrigger = (GcTrigger) Preconditions.checkNotNull(gcTrigger, "gcTrigger");
|
|
this.heapDumper = (HeapDumper) Preconditions.checkNotNull(heapDumper, "heapDumper");
|
|
this.heapdumpListener = (HeapDump.Listener) Preconditions.checkNotNull(listener, "heapdumpListener");
|
|
this.heapDumpBuilder = builder;
|
|
}
|
|
|
|
private void ensureGoneAsync(final long j, final KeyedWeakReference keyedWeakReference) {
|
|
this.watchExecutor.execute(new Retryable() { // from class: com.squareup.leakcanary.RefWatcher.1
|
|
@Override // com.squareup.leakcanary.Retryable
|
|
public Retryable.Result run() {
|
|
return RefWatcher.this.ensureGone(keyedWeakReference, j);
|
|
}
|
|
});
|
|
}
|
|
|
|
private boolean gone(KeyedWeakReference keyedWeakReference) {
|
|
return !this.retainedKeys.contains(keyedWeakReference.key);
|
|
}
|
|
|
|
private void removeWeaklyReachableReferences() {
|
|
while (true) {
|
|
KeyedWeakReference keyedWeakReference = (KeyedWeakReference) this.queue.poll();
|
|
if (keyedWeakReference == null) {
|
|
return;
|
|
} else {
|
|
this.retainedKeys.remove(keyedWeakReference.key);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void clearWatchedReferences() {
|
|
this.retainedKeys.clear();
|
|
}
|
|
|
|
Retryable.Result ensureGone(KeyedWeakReference keyedWeakReference, long j) {
|
|
long nanoTime = System.nanoTime();
|
|
long millis = TimeUnit.NANOSECONDS.toMillis(nanoTime - j);
|
|
removeWeaklyReachableReferences();
|
|
if (this.debuggerControl.isDebuggerAttached()) {
|
|
return Retryable.Result.RETRY;
|
|
}
|
|
if (gone(keyedWeakReference)) {
|
|
return Retryable.Result.DONE;
|
|
}
|
|
this.gcTrigger.runGc();
|
|
removeWeaklyReachableReferences();
|
|
if (!gone(keyedWeakReference)) {
|
|
long nanoTime2 = System.nanoTime();
|
|
long millis2 = TimeUnit.NANOSECONDS.toMillis(nanoTime2 - nanoTime);
|
|
File dumpHeap = this.heapDumper.dumpHeap();
|
|
if (dumpHeap == HeapDumper.RETRY_LATER) {
|
|
return Retryable.Result.RETRY;
|
|
}
|
|
this.heapdumpListener.analyze(this.heapDumpBuilder.heapDumpFile(dumpHeap).referenceKey(keyedWeakReference.key).referenceName(keyedWeakReference.name).watchDurationMs(millis).gcDurationMs(millis2).heapDumpDurationMs(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime2)).build());
|
|
}
|
|
return Retryable.Result.DONE;
|
|
}
|
|
|
|
HeapDump.Builder getHeapDumpBuilder() {
|
|
return this.heapDumpBuilder;
|
|
}
|
|
|
|
Set<String> getRetainedKeys() {
|
|
return new HashSet(this.retainedKeys);
|
|
}
|
|
|
|
boolean isEmpty() {
|
|
removeWeaklyReachableReferences();
|
|
return this.retainedKeys.isEmpty();
|
|
}
|
|
|
|
public void watch(Object obj) {
|
|
watch(obj, "");
|
|
}
|
|
|
|
public void watch(Object obj, String str) {
|
|
if (this == DISABLED) {
|
|
return;
|
|
}
|
|
Preconditions.checkNotNull(obj, "watchedReference");
|
|
Preconditions.checkNotNull(str, "referenceName");
|
|
long nanoTime = System.nanoTime();
|
|
String uuid = UUID.randomUUID().toString();
|
|
this.retainedKeys.add(uuid);
|
|
ensureGoneAsync(nanoTime, new KeyedWeakReference(obj, uuid, str, this.queue));
|
|
}
|
|
}
|