1.LeakCanary一些准备知识
1.1LeakCanart简单介绍
- 由Square开源的一款轻量第三方内存泄漏检测工具
- 原理:watch一个即将销毁的对象
1.2内存
- 栈:基本对象的应用
- 堆:用来存放new的对象
- 方法区:也称为静态区
1.3内存泄漏会导致什么问题
- OOM
1.4android常见内存泄漏
1.4.1.单例造成的内存泄漏
public class SingletonActivityContext {
private static SingletonActivityContext instance;
private Context context;
private SingletonActivityContext(Context context) {
//原因SingletonActivityContext会持有你传入的Activity的实例对象,并会一直伴随着整个生命周期
this.context = context;
}
public static SingletonActivityContext getInstance(Context context) {
if (instance == null) {
instance = new SingletonActivityContext(context);
}
return instance;
}
}
解决办法:
ublic class SingletonAppliationContext {
private static SingletonAppliationContext instance;
private Context context;
private SingletonAppliationContext(Context context) {
this.context = context.getApplicationContext();// 使用Application 的context
}
public static SingletonAppliationContext getInstance(Context context) {
if (instance == null) {
instance = new SingletonAppliationContext(context);
}
return instance;
}
}
1.4.2非静态内部类创建静态实例造成的内存泄漏
public class StaticLeakActivity extends Activity {
private static noneStaticClass mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (mResource == null) {
mResource = new noneStaticClass();
}
}
//noneStaticClass 默认会持有StaticLeakActivity的实例对象,这样就会导致mResource一直持有StaticLeakActivity对象,此时它无法被回收
private class noneStaticClass {
}
}
1.4.3Handler导致的内存泄漏
public class HandlerLeakActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//原因:消息被推送到消息队列中,finish会销毁当前activity,但是因为延迟发送的message还处于主线程中,这时候会持有activity的handler的使用,这时候activity就不会被销毁,因为message持有handler引用,而handler持有activity的引用
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 1000 * 60 * 10);
finish();
}
/*
* 解决办法:
* 1.将 Handler 声明为静态的
* 2.通过弱引用的方式引入 Activity
*
* /
}
1.4.4.线程造成的内存泄漏
public class ThreadLeakActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
testThreadLeak();
}
private void testThreadLeak() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
}.execute();
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
}
}
解决办法
static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakReference<Context> weakReference;
public MyAsyncTask(Context context) {
weakReference = new WeakReference<>(context);
}
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
ThreadLeakActivity activity = (ThreadLeakActivity) weakReference.get();
if (activity != null) {
//...
}
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
SystemClock.sleep(10000);
}
}
1.4.5.Webview造成的内存泄漏
public class WebviewLeakActivity extends AppCompatActivity {
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//mWebView = (WebView) findViewById(R.id.wv_show);
mWebView.loadUrl("http://www.baidu.com");
}
//解决办法
@Override
protected void onDestroy() {
destroyWebView();
android.os.Process.killProcess(android.os.Process.myPid());
super.onDestroy();
}
private void destroyWebView() {
if (mWebView != null) {
mWebView.pauseTimers();
mWebView.removeAllViews();
mWebView.destroy();
mWebView = null;
}
}
}
Leakcanary简单使用
1.原理:
- Activity Destory之后将它放在一个WeakRefrenece
- 这个WeakReference关联到ReferenceQueue
- 查看ReferenceQueue是否存在Actiivty的引用
- 如果该Activity泄露了,Dump出Heap信息,然后再去分析泄漏路径
2.4种应用
- 强应用
- 软应用:内存不足时会被回收
- 弱应用:直接去回收
- 虚应用:和没有应用是一样的
软应用和弱应用相同点;对象被垃圾回收,java虚拟机就会把这个应用加入到与之关联的引用队列中
3.github官网:https://github.com/square/leakcanary
4.gradle依赖:
//官方的
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
//因为我这里是android studio 2.3.3的版本
compile 'com.squareup.leakcanary:leakcanary-android:1.5.4'
5.使用很简单: LeakCanary.install(this);
Leakcanary源码分析
1.install源码分析
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
//分析buildAndInstall
.buildAndInstall();
}
buildAndInstall源码分析
public RefWatcher buildAndInstall() {
//返回refWatcher
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
//弹出告诉你那里泄漏,表示开启activity
LeakCanary.enableDisplayLeakActivity(context);
//继续看这个
ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;
}
ActivityRefWatcher.install源码分析
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
public void watchActivities() {
// 反注册我们以前的activitycallback
stopWatchingActivities();
//重新注册 看lifecycleCallbacks源码
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
lifecycleCallbacks源码很长只要关注一行就可以了
@Override public void onActivityDestroyed(Activity activity) {
//和Application的onDestory的生命周期关联
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
void onActivityDestroyed(Activity activity) {
// 首先看下refWatcher有哪些成员变量,然后看watch的源码
refWatcher.watch(activity);
}
//refWatcher成员变量
//用于内存检测
private final WatchExecutor watchExecutor;
//查询是否正在调试中
private final DebuggerControl debuggerControl;
//用于判断泄漏之前,给gc最后一次机会
private final GcTrigger gcTrigger;
//内存泄漏堆文件
private final HeapDumper heapDumper;
//key
private final Set<String> retainedKeys;
//判断弱应用所持有的对象是否被已经被gc垃圾回收
private final ReferenceQueue<Object> queue;
private final HeapDump.Listener heapdumpListener;
//排除一些系统bug
private final ExcludedRefs excludedRefs;
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
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();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
//看这个源码
ensureGoneAsync(watchStartNanoTime, reference);
}
ensureGoneAsync源码分析
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
//线程
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
//持续时间
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//移除已达到队列中的弱应用
removeWeaklyReachableReferences();
//如果是处于调试中则不进行分析
if (debuggerControl.isDebuggerAttached()) {
return RETRY;
}
if (gone(reference)) {
return DONE;
}
//手动执行gc
gcTrigger.runGc();
//移除已达到队列中的弱应用
removeWeaklyReachableReferences();
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
//最后在线程中取开始分析我们的泄漏
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
analyze分析我们的泄漏
void analyze(HeapDump heapDump);
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
HeapAnalyzerService.runAnalysis源码分析,因为这个方法是继承于IntentService方法,所以会回调onHandlerIntent方法
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);
}
@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);
//排除一些系统bug
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 {
//heapDumpFile封装成对象
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
//解释器解析上面的buffer
HprofParser parser = new HprofParser(buffer);
//具体的解析工作
Snapshot snapshot = parser.parse();
//对我们所检查的结果去重
deduplicateGcRoots(snapshot);
//获得解析的结果
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
if (leakingRef == null) {//表示对象不存在,表示已经清楚
return noLeak(since(analysisStartNanoTime));
}
//找出泄漏的对象/找出泄漏对象的最短路径
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
1.把.hprof转化为Snapshot
2.优化gcroots
3.找出泄漏的对象/找出泄漏对象的最短路径
findLeakingReference源码分析
private Instance findLeakingReference(String key, Snapshot snapshot) {
//根据弱应用查找类的对象
ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
List<String> keysFound = new ArrayList<>();
//遍历这个对象的所有实例
for (Instance instance : refClass.getInstancesList()) {
List<ClassInstance.FieldValue> values = classInstanceValues(instance);
String keyCandidate = asString(fieldValue(values, "key"));
//如果key的值和最开始定义封装的key值相同,那么返回这个泄漏对象
if (keyCandidate.equals(key)) {
return fieldValue(values, "referent");
}
keysFound.add(keyCandidate);
}
findLeakTrace源码分析
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
Instance leakingRef) {
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
if (result.leakingNode == null) {
return noLeak(since(analysisStartNanoTime));
}
//屏幕显示的就是这个
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
String className = leakingRef.getClassObj().getClassName();
snapshot.computeDominators();
Instance leakingInstance = result.leakingNode.instance;
//计算内存泄漏的空间大小的
long retainedSize = leakingInstance.getTotalRetainedSize();
// TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
if (SDK_INT <= N_MR1) {
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
}
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
since(analysisStartNanoTime));
}