性能相關:UI卡頓 / ANR / 內存泄漏——>OOMhtml
內存泄漏的本質:較長生命週期對象持有較短生命週期的引用致使,較短引用無法釋放內存。linux
GcRoots:Garbage Collector 的對象, 收集非GC Roots的引用對象,一般的GC Root有哪些?android
www.jianshu.com/p/dcfe84c50…web
經過System Class Loader或者Boot Class Loader加載的class對象,經過自定義類加載器加載的class不必定是GC Root
處於激活狀態的線程
棧中的對象
JNI棧中的對象
JNI中的全局對象
正在被用於同步的各類鎖對象
JVM自身持有的對象,好比系統類加載器等。
複製代碼
一般這裏涉及的 靜態的對象,其它運行線程持有當前的引用。shell
LeakCanary原理watch一個即將要銷燬的對象:bash
內存泄漏會形成OOM的罪魁禍首 探究源碼,檢測Activity泄漏的機制,LeakCanary的原理網絡
####ReferenceQueue 軟引用/弱引用app
對象被GC回收,Java虛擬機會把它加入到ReferenceQueue中dom
關於ReferenceQueue: www.cnblogs.com/dreamroute/…異步
四種引用類型:
StrongReference
softReference(內存空間不夠時纔回收)
WeakReference()
virtualReference(虛引用)
監控Activity的內存泄漏
LeakCanary.enableDisplayLeakActivity(控制彈框)
建立一個refwatcher,啓動一個ActivityRefWatcher監聽Activity的生命週期的狀況(新版本沒有排除系統Reference的引用)
//Refwatcher類結構
public final class RefWatcher {
public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
private final WatchExecutor watchExecutor;//執行內存泄漏檢測用的
private final DebuggerControl debuggerControl;//查詢是否在代碼調試中,調試的時候就不檢測
private final GcTrigger gcTrigger;//處理GC的,用於判斷泄漏以前給最後一次機會是否會GC,否者會顯示出來
private final HeapDumper heapDumper;//Dump出內存泄漏的堆文件
private final HeapDump.Listener heapdumpListener;//分析產生Heap文件的回調
private final HeapDump.Builder heapDumpBuilder;
private final Set<String> retainedKeys;//待檢測的產生泄漏的Key
private final ReferenceQueue<Object> queue;//引用隊列,判斷弱引用持有的對象是否執行了GC回收
......
}
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
//返回一個Key值,惟一的
String key = UUID.randomUUID().toString();
//加入key到待檢測的隊列當中
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
//開啓異步線程分析弱引用reference
ensureGoneAsync(watchStartNanoTime, reference);
}
複製代碼
經過ActivityLifecycleCallbacks把Activity的ondestory生命週期關聯
原來的ActivityRefWatcher被廢棄了
/** * @deprecated This was initially part of the LeakCanary API, but should not be any more. * {@link AndroidRefWatcherBuilder#watchActivities} should be used instead. * We will make this class internal in the next major version. */
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public final class ActivityRefWatcher {}
複製代碼
換成 AndroidRefWatcherBuilder, 其實最終綁定 ActivityRefWatcher的生命週期
// LeakCanary的入口
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
/** * Creates a {@link RefWatcher} instance and makes it available through {@link * LeakCanary#installedRefWatcher()}. * * Also starts watching activity references if {@link #watchActivities(boolean)} was set to true. * * @throws UnsupportedOperationException if called more than once per Android process. */
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
if (watchActivities) {
//這裏又調用原來廢棄的ActivityRefWatcher的方法
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
//ActivityRefWatcher類下面的
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
//建立activityRefWatcher
ActivityRefWatcher activityRefWatcher =
new ActivityRefWatcher(application,refWatcher);
//綁定生命週期
application.registerActivityLifecycleCallbacks
(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
//調用watch方法,監聽activity的泄漏
refWatcher.watch(activity);
}
};
複製代碼
最後在線程池中去開始分析咱們的泄漏
//開啓線程池分析
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
//容錯性考慮
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
//從watch到GC的時間
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//把已經回收的對象引用從 標記內存泄漏的retainedKeys中清除掉
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {
return DONE;
}
//觸發GC後又會把回收的對象引用加入到Queue中。
gcTrigger.runGc();
removeWeaklyReachableReferences();
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);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
return DONE;
}
//從retainedKeys中去除已經GC掉的對象
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;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
複製代碼
在ServiceHeapDumpListener (implements HeapDump.Listener)開啓真正的分析:
@Override public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
複製代碼
public final class HeapAnalyzerService extends ForegroundService implements AnalyzerProgressListener {
private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
private static final String HEAPDUMP_EXTRA = "heapdump_extra";
public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
setEnabledBlocking(context, HeapAnalyzerService.class, true);
setEnabledBlocking(context, listenerServiceClass, true);
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
ContextCompat.startForegroundService(context, intent);
}
public HeapAnalyzerService() {
super(HeapAnalyzerService.class.getSimpleName(), R.string.leak_canary_notification_analysing);
}
@Override protected void onHandleIntentInForeground(@Nullable 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);
//這裏去除調heapDump.excludedRefs對應的系統的
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
//進一步分析內存
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
}
複製代碼
checkForLeak
findLeakTrace: 找到最短的路勁,找到內存泄漏大小。
/** * Searches the heap dump for a {@link KeyedWeakReference} instance with the corresponding key, * and then computes the shortest strong reference path from that instance to the GC roots. */
public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile, @NonNull String referenceKey, boolean computeRetainedSize) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) {
Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
//1. 將hprof文件轉化成Snapshot快照文件,包含了全部引用對象的路徑。
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
HprofParser parser = new HprofParser(buffer);
listener.onProgressUpdate(PARSING_HEAP_DUMP);
Snapshot snapshot = parser.parse();
listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
//2.刪除重複的GCRoots以及對象
deduplicateGcRoots(snapshot);
listener.onProgressUpdate(FINDING_LEAKING_REF);
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, computeRetainedSize);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
複製代碼
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot, Instance leakingRef, boolean computeRetainedSize) {
listener.onProgressUpdate(FINDING_SHORTEST_PATH);
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
// False alarm, no strong reference path to GC Roots.
if (result.leakingNode == null) {
return noLeak(since(analysisStartNanoTime));
}
listener.onProgressUpdate(BUILDING_LEAK_TRACE);
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
String className = leakingRef.getClassObj().getClassName();
long retainedSize;
if (computeRetainedSize) {
listener.onProgressUpdate(COMPUTING_DOMINATORS);
// Side effect: computes retained size.
snapshot.computeDominators();
Instance leakingInstance = result.leakingNode.instance;
retainedSize = leakingInstance.getTotalRetainedSize();
// TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
if (SDK_INT <= N_MR1) {
listener.onProgressUpdate(COMPUTING_BITMAP_SIZE);
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
}
} else {
retainedSize = AnalysisResult.RETAINED_HEAP_SKIPPED;
}
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
since(analysisStartNanoTime));
}
複製代碼
補充:Application:單例模式
Application應用場景
/** @hide */
//內存級別,對內存進行釋放。
@IntDef(prefix = { "TRIM_MEMORY_" }, value = {
TRIM_MEMORY_COMPLETE,
TRIM_MEMORY_MODERATE,
TRIM_MEMORY_BACKGROUND,
TRIM_MEMORY_UI_HIDDEN,
//內存不足
TRIM_MEMORY_RUNNING_CRITICAL,
TRIM_MEMORY_RUNNING_LOW,
TRIM_MEMORY_RUNNING_MODERATE,
})
複製代碼
onTrimMemory跟 onLowMemory都是內存優化的地方。
MAT以及Android Studio自己的內存監控: blog.csdn.net/u012760183/…
網絡請求流量
經過運營商的網絡訪問Internet。
平常開發中能夠經過tcpdump + Wireshark抓包測試法
TrafficStats類:getMobileTxPackets, getMobileRxPackets, getMobileTxBytes, getMobileRxBytes
(讀取linux文件系統的)
冷啓動
adb shell am start -W packagename /MainActivity
日誌打印:起點 ->終點
起點:Application的onCreate方法
終點:首頁ActivityOncreate加載完成
Fps:
Choreographer:經過日誌監控掉幀現象。
Vsync: 同步信號,硬件終端
流暢度:實際幀率/理論幀率