前文内存分析工具集中介绍了一系列的内存分析工具及其基本使用, 诸如Memory Monitor, HPROF Viewer, MAT等等. 实际上了解了工具的使用, 咱们就已经掌握了如何分析内存问题了.javascript
为了能对工具的使用更加深刻, 本篇将一个代码片断为例, 从时序的角度讲解下如何使用这些工具来分析一个内存泄露.html
系列文:
1.GC那些事儿
2.Android的内存管理
3.内存分析工具
4.内存泄露实例分析java
假设有一个单例的ListenerManager, 能够add / remove Listener, 有一个Activity, 实现了该listener, 且这个Activity中持有大对象BigObject, BigObject中包含一个大的字符串数组和一个Bitmap List.android
代码片断以下:git
ListenerManager程序员
public class ListenerManager {
private static ListenerManager sInstance;
private ListenerManager() {}
private List<SampleListener> listeners = new ArrayList<>();
public static ListenerManager getInstance() {
if (sInstance == null) {
sInstance = new ListenerManager();
}
return sInstance;
}
public void addListener(SampleListener listener) {
listeners.add(listener);
}
public void removeListener(SampleListener listener) {
listeners.remove(listener);
}
}复制代码
MemoryLeakActivitygithub
public class MemoryLeakActivity extends AppCompatActivity implements SampleListener {
private BigObject mBigObject = new BigObject();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memory_leak);
ListenerManager.getInstance().addListener(this);
}
@Override
public void doSomething() {
}
}复制代码
具体代码参看Github.数组
根据前文的工具介绍, Android Studio自带了Memory Monitor, HPROF Viewer & Analyzer来分析内存使用及内存问题.android-studio
启动咱们要检测的Activity(MemoryLeakActivity), 而后退出, 在monitor中查看内存变化:
app
第一步
点击"Analyzer Tasks"视图中的启动按钮, 启动分析
第二步
查看"Analysis Result"中的分析结果, 点击"Leaked Activityes"中的具体实例, 该实例的引用关系将会展现在"Reference Tree"视图中.
第三步
根据"Reference Tree"视图中的引用关系找到是谁让这个leak的activity活着的, 也就是谁Dominate这个activity对象.
此例中, 比较简单, 能够很清晰看到是ListenerManager的静态单例sInstance最终支配了MemoryLeakActivity. sIntance链接到GC Roots, 故而致使MemoryLeakActivity GC Roots可达, 没法被回收.
上述步骤, 可让咱们快速定位可能的内存泄露. 固然, 内存问题, 除了内存泄露, 还有内存消耗过大. 咱们能够在Heap Viewer中查看分析内存的消耗点, 以下:
就单纯的分析Android App的内存使用和内存泄露来讲, 我的以为Android Studio自带的工具已经足够好了, 并且再持续变得更好, 也更便于Android的开发人员去理解. 故而其实一开始在Android性能分析工具一文中, 我就没有详细去提MAT. 相对与Android Studio中的Memory Monitor, HPROF工具来讲, MAT的使用显得更加生涩, 难以理解.
关于MAT的帮助文档, 我的翻译了一份, 须要的同窗戳这里.
固然, 若是咱们想对内存的使用相关知识了解得更多, 仍是有必要了解下MAT的...
下面咱们以几个角度来了解下MAT的基本使用:
再次:
Android Studio导出的hprof文件须要转换下才能够在MAT中使用.
$ hprof-conv com.anly.samples_2016.10.31_15.07.hprof mat.hprof复制代码
MAT中不少视图的第一行, 均可以输入正则, 来匹配咱们关注的对象实例.
对于Android App开发来讲, 大部分的内存问题都跟四大组件, 尤为是Activity相关, 故而咱们会想查出全部Activity实例的内存占用状况, 可使用OQL来查询:
具体OQL语法看这里.
上面几个视图均可以让咱们很快速的找到内存的消耗点, 接下来咱们要分析的就是为什么这些个大对象没有被回收.
根据第一弹:GC那些事儿所言, 对象没有被回收是由于他有到GC Roots的可达路径. 那么咱们就来分析下这条路径(Path to GC Roots), 看看是谁在这条路中"搭桥".
以下, 进入该对象的"path2gc"视图:
一样, 与HPROF Analyzer殊途同归, 找出了是ListenerManager的静态实例致使了MemoryLeakActivity没法回收.
大道至简, 程序员都应该"懒", 故而咱们都但愿有更方便快捷的方式让咱们发现内存泄露. 伟大的square发挥了这一优良传统, LeakCanary面世.
app的build.gradle中加入:
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}复制代码
Application中加入:
public class SampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}复制代码
当发生可疑内存泄露时, 会在桌面生成一个"Leaks"的图标, 点击进去能够看到内存泄露的疑点报告:
能够看到, 结果与前两者的分析结果"惊人"一致, 不一致就出事儿了, :)
足够方便且直观吧, 赶快用起来吧.
固然, 内存问题不单单是内存泄露, 还有内存占用过多等, 这时咱们就须要借助前两种工具了.
综上, 建议LeakCanary集成做为App的必选项, 大多数状况下咱们能够用LeakCanary结合Android Studio自带的工具分析内存问题.
若是有精力, 仍是建议深刻了解下MAT, 能让咱们更深刻了解GC的机制, 相关概念, MAT还有不少更高端的功能值得咱们探索, 例如Heap比较等.
其实, 内存问题的分析, 无外乎分析对象的内存占用(Retained Size), 找出Retained Size大的对象, 找到其直接支配(Immediate Dominator), 跟踪其GC可达路径(Path to GC Roots), 从而找到是谁让这个大对象活着. 找到问题症结, 对症下药.