性能相关:UI卡顿 / ANR / 内存泄漏——>OOM
内存泄漏的本质:较长生命周期对象持有较短生命周期的引用导致,较短引用没法释放内存。
GcRoots:Garbage Collector 的对象, 收集非GC Roots的引用对象,通常的GC Root有哪些?
通过System Class Loader或者Boot Class Loader加载的class对象,通过自定义类加载器加载的class不一定是GC Root
处于激活状态的线程
栈中的对象
JNI栈中的对象
JNI中的全局对象
正在被用于同步的各种锁对象
JVM自身持有的对象,比如系统类加载器等。
通常这里涉及的 静态的对象,其它运行线程持有当前的引用。
LeakCanary原理watch一个即将要销毁的对象:
- 栈(stack)
- 堆(heap)
- 方法区(method)
常见的内存泄漏:
- 单例持有context, 写成 ApplicationContext
- 非静态内部类创建静态实例造成的内存泄漏(改成静态的内部类)
- Handler(TLS,handler生命周期跟Activity的生命周期不一样) handler.postDelay延迟发送。原因message 持有handler,handler持有Activity,将Handler设置为静态的(以弱引用的方式持有Activity)
- 线程的内存泄漏。AsyncTask,Thread+Runnable,以及Handler(将他们定义为static, 调用AsyncTask的Cancel方法)
- Webview,hybird。webview加载网页申请native内存加载页面,(1.将webview放在单独的Webview的进程里; 2. 在Webview所在的Activity在onDestory的时候killProcess)
LeakCanary源码:
内存泄漏会造成OOM的罪魁祸首 探究源码,检测Activity泄漏的机制,LeakCanary的原理
Activity泄漏检测原理
- 将Activity Destory之后将它放在一个WeakReference
- 将这个WeakReference放到引用队列ReferenceQueue
####ReferenceQueue 软引用/弱引用
对象被GC回收,Java虚拟机会把它加入到ReferenceQueue中
关于ReferenceQueue: www.cnblogs.com/dreamroute/…
四种引用类型:
StrongReference
softReference(内存空间不够时才回收)
WeakReference()
virtualReference(虚引用)
RefWatcher
监控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);
}
}
- 将.hprof转化成 SnapShot
- 优化GCRoots
checkForLeak
- 解析dump下文件的hprof,把dump文件parse成Snapshot文件
- 根据前面的弱引用定义的
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应用场景
- 初始化 全局对象、环境配置变量
- 获取应用当前的内存情况
- 监听应用程序内 所有Activity的生命周期
- 内存监控 (onTrimMemory)TrimMemoryLevel、onLowMemory、onTerminate()、onConfigurationChanged
/** @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/…
网络流量和冷启动
- 整体的性能解决思路
- 应用性能类型
- 各种性能数据指标
性能解决思路
- 监控性能指标,量化指标
- 根据上报统计信息
- 持续监控并观察
应用性能种类
- 资源消耗
- 流畅度(网络请求、UI绘制、冷启动)
各个性能数据指标
-
网络请求流量
通过运营商的网络访问Internet。
-
日常开发中可以通过tcpdump + Wireshark抓包测试法
-
TrafficStats类:getMobileTxPackets, getMobileRxPackets, getMobileTxBytes, getMobileRxBytes
(读取linux文件系统的)
-
-
冷启动
adb shell am start -W packagename /MainActivity
日志打印:起点 ->终点
起点:Application的onCreate方法
终点:首页ActivityOncreate加载完成
- UI卡顿Fps帧率
Fps:
Choreographer:通过日志监控掉帧现象。
Vsync: 同步信号,硬件终端
流畅度:实际帧率/理论帧率