美文网首页Android探索之路APP的优化内存管理
内存泄露实例分析 -- Android内存优化第四弹

内存泄露实例分析 -- Android内存优化第四弹

作者: anly_jun | 来源:发表于2016-10-31 18:18 被阅读4381次
    cover

    引言

    前文内存分析工具集中介绍了一系列的内存分析工具及其基本使用, 诸如Memory Monitor, HPROF Viewer, MAT等等. 实际上了解了工具的使用, 我们就已经掌握了如何分析内存问题了.

    为了能对工具的使用更加深入, 本篇将一个代码片段为例, 从时序的角度讲解下如何使用这些工具来分析一个内存泄露.

    系列文:
    1.GC那些事儿
    2.Android的内存管理
    3.内存分析工具
    4.内存泄露实例分析

    1, 例子

    假设有一个单例的ListenerManager, 可以add / remove Listener, 有一个Activity, 实现了该listener, 且这个Activity中持有大对象BigObject, BigObject中包含一个大的字符串数组和一个Bitmap List.

    代码片段如下:

    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);
        }
    }
    

    MemoryLeakActivity

    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.

    2, 使用Android Studio的自带工具来分析

    根据前文的工具介绍, Android Studio自带了Memory Monitor, HPROF Viewer & Analyzer来分析内存使用及内存问题.

    2.1 查看Memory使用, 并导出hprof文件

    启动我们要检测的Activity(MemoryLeakActivity), 然后退出, 在monitor中查看内存变化:


    2.2 在HPROF Viewer界面, 开始分析

    第一步

    点击"Analyzer Tasks"视图中的启动按钮, 启动分析

    第二步

    查看"Analysis Result"中的分析结果, 点击"Leaked Activityes"中的具体实例, 该实例的引用关系将会展示在"Reference Tree"视图中.

    第三步

    根据"Reference Tree"视图中的引用关系找到是谁让这个leak的activity活着的, 也就是谁Dominate这个activity对象.

    此例中, 比较简单, 可以很清晰看到是ListenerManager的静态单例sInstance最终支配了MemoryLeakActivity. sIntance连接到GC Roots, 故而导致MemoryLeakActivity GC Roots可达, 无法被回收.

    关于Dominate, GC Roots, GC Roots可达, 活对象等概念, 请结合前两篇的理论文1, 2观看.

    2.3 使用Heap Viewer查看内存消耗

    上述步骤, 可以让我们快速定位可能的内存泄露. 当然, 内存问题, 除了内存泄露, 还有内存消耗过大. 我们可以在Heap Viewer中查看分析内存的消耗点, 如下:

    3, MAT让我们看的更多

    就单纯的分析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
    

    3.1 Histogram视图定位内存消耗

    MAT中很多视图的第一行, 都可以输入正则, 来匹配我们关注的对象实例.

    3.2 Dominate Tree视图查看支配关系

    3.3 使用OQL查询相关对象

    对于Android App开发来说, 大部分的内存问题都跟四大组件, 尤其是Activity相关, 故而我们会想查出所有Activity实例的内存占用情况, 可以使用OQL来查询:


    具体OQL语法看这里.

    3.4 GC路径定位问题

    上面几个视图都可以让我们很快速的找到内存的消耗点, 接下来我们要分析的就是为何这些个大对象没有被回收.

    根据第一弹:GC那些事儿所言, 对象没有被回收是因为他有到GC Roots的可达路径. 那么我们就来分析下这条路径(Path to GC Roots), 看看是谁在这条路中"搭桥".

    如下, 进入该对象的"path2gc"视图:
    ![Slice 1](http://oat9lzupi.bkt.clouddn.com/Slice 1.jpg)

    同样, 与HPROF Analyzer异曲同工, 找出了是ListenerManager的静态实例导致了MemoryLeakActivity无法回收.

    4, LeakCanary让内存泄露无处可藏

    大道至简, 程序员都应该"懒", 故而我们都希望有更方便快捷的方式让我们发现内存泄露. 伟大的square发挥了这一优良传统, LeakCanary面世.

    4.1 加入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);
        }
    }
    

    4.2 操作要检测的界面, 查看结果

    当发生可疑内存泄露时, 会在桌面生成一个"Leaks"的图标, 点击进去可以看到内存泄露的疑点报告:


    可以看到, 结果与前二者的分析结果"惊人"一致, 不一致就出事儿了, :)

    足够方便且直观吧, 赶快用起来吧.
    当然, 内存问题不仅仅是内存泄露, 还有内存占用过多等, 这时我们就需要借助前两种工具了.

    结语

    综上, 建议LeakCanary集成作为App的必选项, 大多数情况下我们可以用LeakCanary结合Android Studio自带的工具分析内存问题.

    如果有精力, 还是建议深入了解下MAT, 能让我们更深入了解GC的机制, 相关概念, MAT还有很多更高端的功能值得我们探索, 例如Heap比较等.

    其实, 内存问题的分析, 无外乎分析对象的内存占用(Retained Size), 找出Retained Size大的对象, 找到其直接支配(Immediate Dominator), 跟踪其GC可达路径(Path to GC Roots), 从而找到是谁让这个大对象活着. 找到问题症结, 对症下药.

    相关文章

      网友评论

      • 景阳_jy:在HPROF Viewer中通过Analyzer Tasks分析,在结果中找不到Leaked Activities,通过集成LeakCanary,可以捕获到泄漏信息;这是为啥,是我Dump java Heap的时机不对吗?
      • imesong:很赞
      • 会理发的店小二:写的很清楚,也很容易让人理解,先收藏了.

      本文标题:内存泄露实例分析 -- Android内存优化第四弹

      本文链接:https://www.haomeiwen.com/subject/gqlputtx.html