一、内存泄漏介绍
定义:内存中存在已经没有用的对象
表现:内存抖动、可用内存逐渐减少
危害:内存不足、GC 频繁、OOM
二、内存泄漏解决实战
Memory Profiler 只能提供简单的分析,并不能确认问题,所以我们用 Memory Analyzer
(简称 MAT)来确认问题。
Memory Analyzer 下载链接
转换:hprof-conv 原文件路径 转换后文件路径 (如:hprof-conv /Users/wuchao/AndroidStudioProjects/imooc/Performance/memory-leak.hprof /Users/wuchao/AndroidStudioProjects/imooc/Performance/memory-leak_trans.hprof )
下面是实例代码:
/**
* @desciption: 模拟内存泄露的Activity
*/
public class MemoryLeakActivity extends AppCompatActivity implements CallBack {
private AppCompatImageView mIvMemoryleak;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memory_leak);
initView();
}
private void initView() {
mIvMemoryleak = (AppCompatImageView) findViewById(R.id.iv_memoryleak);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.splash);
mIvMemoryleak.setImageBitmap(bitmap);
CallBackManager.addCallBack(this);
}
@Override
public void dpOperate() {
}
}
public class CallBackManager {
public static ArrayList sCallBacks = new ArrayList<>();
public static void addCallBack(CallBack callBack) {
sCallBacks.add(callBack);
}
public static void removeCallBack(CallBack callBack) {
sCallBacks.remove(callBack);
}
}
public interface CallBack {
void dpOperate();
}
为了实现内存泄漏的效果,我们反复进入 MemoryLeakActivity 这个类。则 Memory Profiler 的分析图,如下图:
从图中可以看到内存呈阶梯状上升,也就是说可用内存逐渐减少,则可以断定页面出现了内存泄漏。但是 Memory Profiler 工具不能断定哪个地方有内存泄漏,这时我们需要用 Memory Analyzer 工具。
在 Memory Profiler 界面点击Dump Java heap
按钮,如下图红框中所示:
然后点击保存按钮,如下图红框中所示:
由于保存的文件不能直接使用,需要对这个保存的文件进行转换:
转换:hprof-conv 原文件路径 转换后文件路径 (如:hprof-conv /Users/wuchao/AndroidStudioProjects/imooc/Performance/memory-leak.hprof /Users/wuchao/AndroidStudioProjects/imooc/Performance/memory-leak_trans.hprof )
转换过程如下图所示:
然后由 Memory Analyzer 工具打开转换后的文件,如下图:
Histogram 菜单列出了内存中存活的所有对象,如下图:
由于 Histogram 菜单中内容较多,需要搜索匹配 MemoryLeakActivity。如下图:
从图中可以看出内存中竟然有 8 个 MemoryLeakActivity 对象。这是很不合理的。
接下来我们要找那些强引用引用了 MemoryLeakActivity 类,如下图操作流程:
with incoming references:表示查看该类被哪些类所引用
点击 with incoming references
后界面如下:
然后找到 Path TO GC Roots
,然后选择去掉所有引用(可根据情况选择其他选项),如下图所示步:
结果如下图:
从图中可以看出 MemoryLeakActivity
类被 CallBackManager
类中的 sCallBacks
属性引用。
我们进入 CallBackManager 类中
public class CallBackManager {
public static ArrayList sCallBacks = new ArrayList<>();
public static void addCallBack(CallBack callBack) {
sCallBacks.add(callBack);
}
public static void removeCallBack(CallBack callBack) {
sCallBacks.remove(callBack);
}
}
sCallBacks 被 static 所修饰,它的生命周期和整个 APP 一致。
解决方案:
在 MemoryLeakActivity 类中加入以下代码即可:
@Override
protected void onDestroy() {
super.onDestroy();
CallBackManager.removeCallBack(this);
}
在 MemoryLeakActivity 销毁时将它自己从 CallBackManager 中移除。这样我们的内存泄漏就解决完了。
总结
使用 Memory Profiler 初步观察
可用内存逐渐减少,然后断定可能有内存泄漏通过 Memory Analyzer 结合代码确认
Android 内存抖动介绍及实例演示
参考
全面理解MAT(Memory Analyzer)