Reference概述
Reference
Reference主要負責內存的一個狀態,Reference類把內存分爲四種狀態:
- Active:內存一開始分配的都是Active
- Pending:快要放進隊列的對象,馬上回收的對象。
- Enqueued:對象已經被回收了,已經對象放入隊列中了。
- Inactive:最終狀態,不能再變爲其他狀態。
ReferenceQueue
引用隊列,在對象的可達性更改後,垃圾回收器將已註冊的引用對象添加到隊列中。
舉個例子
檢測一個對象是否被回收,可以使用Reference + ReferenceQueue
- 創建一個引用隊列 queue
- 創建 Refrence 對象,並關聯引用隊列 queue
- 在 reference 被回收的時候,refrence 會被添加到 queue 中
創建一個引用隊列
ReferenceQueue queue = new ReferenceQueue();
// 創建弱引用,此時狀態爲Active,並且Reference.pending爲空,當前Reference.queue = 上面創建的queue,並且next=null
WeakReference reference = new WeakReference(new Object(), queue);
System.out.println(reference);
// 當GC執行後,由於是虛引用,所以回收該object對象,並且置於pending上,此時reference的狀態爲PENDING
System.gc();
/* ReferenceHandler從pending中取下該元素,並且將該元素放入到queue中,此時Reference狀態爲ENQUEUED,Reference.queue = ReferenceENQUEUED */
/* 當從queue裏面取出該元素,則變爲INACTIVE,Reference.queue = Reference.NULL */
Reference reference1 = queue.remove();
System.out.println(reference1);
LeakCanary原理
檢測泄漏的步驟
- 第一步,獲得activity被銷燬的信息,也就是要監聽到activity的生命週期方法。
- 第二步,判斷該對象(activity)是否被回收。若回收了那就是沒泄漏,若判斷該對象沒被回收,說明該對象泄漏了。
- 進行第三步,展示泄漏信息。
LeakCanary源碼
第一步——監聽
使用上只需在Application.onCreate()中調用一句LeakCanary.install(this)即可。
進入install方法
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
標準的建造者模式
- listenerServiceClass、excludedRefs 都是給Builder成員變量賦值
最後的buildAndInstall()方法
public RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
//調用build方法構造
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
if (watchActivities) {
//傳入build好的RefWatch,調用靜態方法進行install
ActivityRefWatcher.install((Application) context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
完全是build設計模式,構造出一個RefWatcher
接下來進入install((Application) context, refWatcher)
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
構造ActivityRefWatcher並調用watchActivities()監控activity
那麼,我們想想作爲第三方應用來說,如何監控應用內的每一個Activity?
可以通過系統提供給我們的接口ActivityLifecycleCallbacks,該接口的回調方法與activity生命週期回調方法對應,每當有activity創建或銷燬時,可以通過回調方法通知外部
watchActivities方法
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
lifecycleCallbacks:
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState){}
@Override public void onActivityStarted(Activity activity) {}
@Override public void onActivityResumed(Activity activity) {}
@Override public void onActivityPaused(Activity activity) {}
@Override public void onActivityStopped(Activity activity) {}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
- 這裏只監控了activityDestory,當有activity被銷燬時,交給onActivityDestoryed()處理
onActivityDestory方法:
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
交給watcher處理。到此,完成了第一步:監聽activity銷燬信息.
接下來研究watch()方法是如何判斷activity是否泄漏的
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
這裏將activity、唯一生成的key、還有queue(是一個ReferenceQueue) 構造一個KeyedWeakReference。並將key值存入retainKeys中保管。該類是WeakReference的子類,如下:
final class KeyedWeakReference extends WeakReference
public final String key;
public final String name;
KeyedWeakReference(Object referent, String key, String name,
ReferenceQueue
super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
this.key = checkNotNull(key, "key");
this.name = checkNotNull(name, "name");
}
}
這裏,我們將activity包裝弱引用,並添加了referenceQueue,那麼當該activity被GC回收時,我們就可以從referenceQueue中獲取該activity的reference。
第二步——檢測泄漏(核心)
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//將已被回收的activity對象的keyedWeakReference的key值從retainedKeys中刪除,以達到
//過濾目的
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
//如果retainedKeys中不存在reference,說明它已經被回收,返回
if (gone(reference)) {
return DONE;
}
//手動調用GC
gcTrigger.runGc();
//再次過濾
removeWeaklyReachableReferences();
//若retainedKeys中還存在該reference(還沒有被濾掉),則判斷爲該reference泄漏,進行下一步dump內存快照
//展示泄漏信息
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
總結一下
- 1、首先將已回收的activity對象的keyedWeakReference的key值從retainedKeys中刪除
- 2、如果retainedKeys中不存在reference的key,說明已經回收,返回
- 3、手動執行GC,然後執行 1 再次過濾已經回收的reference
- 4、再次進行 2 ,若retainedKeys中還存在該reference,進行下一步dump內存快照
removeWeakReachableReferences()方法
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
//若queue中存在該keyedWeakReference,則說明該keyedWeakReference對應的activity已被回收
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
//從retainedKeys中移除,則retainedKeys剩下的就是泄漏的
retainedKeys.remove(ref.key);
}
}
其邏輯就是在queue裏面的到key,然後從retainedKeys中移除。
gone()方法:
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
就是一個contains判斷
第三步——泄漏分析
第三步:展示泄漏信息,也就是獲取dumpheap以及對這個dumpheap進行analyze。
調用比較簡單,而邏輯實現又是依託另外一個項目:HAHA。
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key,
reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
這裏的heapdumpListener是第一步構建watcher時listenerServiceClass(DisplayLeakService.class)裏面創建的
最後是ServiceHeapDumpListener這個類封裝了analyze邏輯:
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
runAnalysis()方法中就是啓動一個服務處理heapDump
public static void runAnalysis(Context context, HeapDump heapDump,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
context.startService(intent);
}
HeapAnalyzerService.onHandIntent()中收到啓動服務請求,調用checkForLeak
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
checkForLeak()方法:
public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) {
Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
HprofParser parser = new HprofParser(buffer);
//將heapDumpFile轉化爲Snapshot
Snapshot snapshot = parser.parse();
deduplicateGcRoots(snapshot);
//找到泄漏對象引用
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
// False alarm, weak reference was cleared in between key check and heap dump.
if (leakingRef == null) {
return noLeak(since(analysisStartNanoTime));
}
//返回泄漏對象的最短路徑
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
主要有以下幾個步驟:
- 把.hprof轉爲Snapshot,這個Snapshot對象就包含了對象引用的所有路徑
- 精簡gcroots,把重複的路徑刪除,重新封裝成不重複的路徑的容器
- 找出泄漏的對象
- 找出泄漏對象的最短路徑
其中第三步的findLeakingReference方法
private Instance findLeakingReference(String key, Snapshot snapshot) {
//找到快照中的KeyedWeakReference類對象
ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
List<String> keysFound = new ArrayList<>();
//遍歷這個類的所有實例
for (Instance instance : refClass.getInstancesList()) {
List<ClassInstance.FieldValue> values = classInstanceValues(instance);
//key值和最開始定義封裝的key值相同,說明該實例是泄漏對象
String keyCandidate = asString(fieldValue(values, "key"));
if (keyCandidate.equals(key)) {
return fieldValue(values, "referent");
}
keysFound.add(keyCandidate);
}
throw new IllegalStateException(
"Could not find weak reference with key " + key + " in " + keysFound);
}