在上一篇文章Android内存优化(一):Java内存区域中已经大体上介绍了Java中的内存分布情况,这一篇主要讲一下内存泄漏的产生原因、内存泄漏的危害、内存泄漏一键分析与定位、以及代码中常见的内存泄漏。

#1内存泄漏的产生原因 前方高能,18岁以下请避让!!! 惊天大咪咪:内存泄漏产生的原因是对象占着茅坑不拉屎!!! 有必要讲一下Android中的垃圾收集是怎么进行的,Android中使用标记-清除(Mark-Sweep)算法进行垃圾回收(garbage collection,简称GC),就是按照正常套路来说,在坑位(内存)不够的情况下,垃圾收集器会遍历全部对象,看哪些对象是可以被回收掉腾出内存的,这个过程称为Mark(标记),Mark的时候要求除了垃圾收集线程之外,其它的线程都停止,这种吊炸天的现象在垃圾收集算法中称为Stop The World,世界围着他转,这就造成了我们的程序会卡顿,但是一般情况下这个时间就几十毫秒,我根本就感受不到好吗。Mark完之后,就是释放内存空间啦,这个过程称为Sweep(清除)。

这一切看起来很美好,但是就是有内存泄漏发生,所以得提一下,不是所有的对象都是特仑苏,阿呸,不是所有的对象都能被回收的,比如下面的傲娇贱货。

  • 垃圾回收的原则:被全局变量(static)、栈变量和寄存器等直接引用和间接引用的对象不能被回收。所以说,对象即使已经使用完,但却一直被其它对象引用,就会导致这个对象无法被回收,造成内存的浪费,让别的对象无屎可拉。对象无法被GC回收就是造成内存泄露的原因!

#2内存泄漏的可能会造成的创伤 如果不是利用工具去找的话,一般情况下内存泄漏是比较难发现的,因为Java中不会报内存泄漏这种异常,所以在轻微的内存泄漏表面上看是跟正常情况下没有区别的。

  • 2.1 内存泄漏跟内存溢出(OOM)的区别就是:**量变和质变。**一个两个内存泄漏表面看起来没毛病,但是量变可以导致质变,内存泄漏多了会炸的,就是报OOM异常,应用直接崩溃,连解释的机会都没有。
  • 2.2 堆得内存大小是确定的,出现内存泄漏后可用的内存会减小,这又会造成垃圾回收的频率加剧,上面提到过,垃圾回收的Mark阶段会有一种吊炸天的现象,就是Stop The World,除了垃圾回收线程之外的线程会停止,频繁的垃圾回收卡顿明显的感受到。
  • 2.3 应用后台运行的时候,内存占用大,进程被系统杀死的概率就会大咯。

#3内存泄漏的发现 内存泄漏的分析的话,必须使用工具才行,庆幸的是,各路大神已经给我们提供了很多强大的内存分析工具,我这里只会讲最方便的。这里提供几个套餐供选择 ####3.1 套餐一:Studio自带Heap Viewer 想不想知道你的应用到底有没有内存泄漏呢?说真,就一分钟的事。

  • 3.1.1打开Studio,连上你的应用,然后Android Monitor (1)->Monitors(2)->Memory,上面有四个图标,暂停图标是开启内存使用状态追踪的开关,默认是开启的,小车图标就是手动GC(3),向下箭头图标(4)是查看堆的分配情况,最后的图标allocation tracker用来跟踪内存分配情况。

  • 3.1.2我讲一下我的使用方式,在应用中操作,从activity1跳转到activity2,然后跳回到activity1界面,这样是为了分析activity2是否会产生内存泄漏。接下来就是真刀真枪的干了。

  • 3.1.3点击小车图标(3),手动GC进行垃圾回收,这样才能更准确的判断activity2是否有内存泄漏发生,最后点击向下箭头图标(4),Studio会自动生成hprof文件并自动展示在Studio界面中。

  • 3.1.4这个就是内存的分析文件了,点击Analyzer Tasks(5),这是让Studio帮我们自动分析是否出现内存泄漏。

  • 3.1.5勾上Detect Leaked Activities(6),最后运行(7)就出现分析结果了

  • 3.1.6看到没,activity2出现内存泄漏了(8),左下角是引用树(9),通过引用树就可以定位到内存泄漏的具体信息了。

####3.2套餐二:Heap Viewer + MAT 是啊,发现有内存泄漏了,然而还有其它的选择,这里就必须使用到其它的工具进行辅助了。

MAT(Memory Analyzer)内存分析工具,这个工具的使用我只简单讲一下,因为我一般不用,不要问为什么,因为用起来比较麻烦一些。

  • 3.2.1MAT下载,进入下载的官网,我电脑是64位的,所以选择Windows(x86_64),整个下载安装流程跟一般软件没啥区别,进入新页面然后点击DOWNLOAD 点击click here就可以下载使用了
  • 3.2.2 hprof文件导入,这个文件的获取流程跟内存泄漏的发现流程基本一样,按上面说的通过Studio的Heap工具获取的,但是文件导入前需要进行一下转换,因为MAT工具不能直接使用,转换也 不麻烦,Studio已经帮你简化这个过程,一键导出转换文件,请看过来
  • 3.2.3 用MAT打开hprof的转换文件,其中Histogram和Dominator Tree比较常用,分析内存泄漏特别需要用到Histogram的两份文件对比分析,就是获取两份内存泄漏前后的hprof转换文件
  • 3.2.3 标题栏Window->Navigator History,打开 Navigator History面板,然后点击打开Histogram,![](http://upload-images.jianshu.io/upload_images/4821599-205d2c4bd313d070.png?imageMogr2/auto-orient/strip%7![Uploading 内存8_883765.png . . .] CimageView2/2/w/1240)
  • 3.2.4 右键histogram,将两份分析文件的 Histogram结果都添加到 Compare Basket中,点击右上角的!图标就会生成对比文件
  • 3.2.5 这就是最后生成的对比文件,你还可以自己选择对比的方式,红圈里面提供不同的对比方式,这样就可以很直观的看出差异,因为我对比的是同一份文件,所以对象间木有差异。

####3.3套餐三:Leakcanary square的开源内存泄漏分析框架,好用得不得了,配置很简单

  • 3.3.1建议在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'}```
- 3.3.2在你的```Application```中的```onCreate()```方法中进行初始化
复制代码

public class ExampleApplication extends Application {

@Override public void onCreate() { super.onCreate(); if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } LeakCanary.install(this); // Normal app init code... } }

- 3.3.3然后,就没有然后了,编译完后运行你的项目,会在项目安装成功后出现附加的组件,里面会展示具体的内存泄漏路径。
![](http://upload-images.jianshu.io/upload_images/4821599-68cfb470f5cf3bb0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 3.3.4通过这个泄漏路径,就对应进行内存泄漏的原因进行分析了,你也可以通过输出的日志进行内存泄漏的定位。
![](http://upload-images.jianshu.io/upload_images/4821599-cd2b7b28aed9223e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>注:到这里3个套餐已经讲完了,关于MAT这个套餐我只是讲一下基本的使用,其实已经够用了,怎么说呢,用起来比较麻烦,所以我自己本身也很少用,我就按自己的使用对比一下三者。
套餐三>套餐一>套餐二
1.套餐三使用最方便,一劳永逸,解析hprof的速度有点慢,但是因为后台自动解析,所以基本上没多大关系;
2.套餐一使用最快,切换一下页面分分钟就知道有没有内存泄漏,但是需要你每一次都要手动操作;
3.套餐三最麻烦,耗时耗力,但是自动分析工具并不能保证找出所有的内存泄漏,这个时候就需要通过MAT辅助分析了。#4代码里头内存泄漏的常见原因
代码中内存泄漏大多数产生的原因是不遵循activity的生命周期。
- 4.1单例模式(静态activity):在你的Activity中定义了一个 static 变量引用了activity,因为static变量的生命周期和app一样长,就算activity被销毁,activity对象还是会被static变量持有,一直到app被销毁,这也是单例模式最容易造成泄漏的原因,如果静态的单例对象持有activity对象的引用,就会使得该对象不能被正常回收,从而导致了内存泄漏。解决办法是使用Application的Context代替activity的context;
复制代码

/**

  • 单例模式 */ public class SingletonClass{     private static SingletonClass instance; private Context context;     public static SingletonClass getInstance(Context context){             synchronized(SingletonClass.class){                 if(instance==null){                     instance=new SingletonClass(Context context);                 }             }         return instance;     }     private SingletonClass(Context context){ this.context = context; //传入activity的context就会造成内存泄露咯 } }
- 4.2静态View:当一个view 被加入到界面中时,它就会持有 context 的强引用,也就是我们的 activity。如果我们通过一个static成员变量引用了这个 view,相当于直接引用了 activity,然后就泄漏了;
复制代码

private static View view; view = findViewById(R.id.sv_button);

- 4.3非静态内部类:我们都知道,内部类能够引用外部类的成员,这正是内部类的好处所在,但是恰恰是这个优势会导致activity内存泄漏,因为非静态内部类默认持有外部类的引用。如果我们创建了一个内部类的对象,并且通过静态变量持有这个对象,就会导致内存泄漏;
复制代码
    private static InnerClass inner = new InnerClass();class InnerClass {}
复制代码
- 4.4匿名内部类:匿名类同样会持有定义它们的对象的引用,如果在 activity 内定义了一个匿名的 AsyncTask 对象,就有可能发生内存泄漏了。因为在activity被销毁之后AsyncTask可能仍然在运行,这样只能等到AsyncTask执行结束才能回收activity;
复制代码

new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { while(true); } }.execute();

- 4.5Handler+Runnable:定义一个匿名的 Runnable 对象并将其提交到 Handler 上也可能导致 activity 泄漏。Runnable对象引用了定义它的 activity 对象,而它会被提交到 Handler 的 MessageQueue 中,如果它在 activity 销毁时还没有被处理,那就会导致内存泄漏了。
复制代码

new Handler() { @Override public void handleMessage(Message message) { super.handleMessage(message); } }.postDelayed(new Runnable() { @Override public void run() { while(true); } }, 1000);

- 4.6Thread:原因类似4.5,尽管是在单独的线程执行任务,但是线程还是会默认持有外部对象,任务没有执行完成就不会释放持有的引用;
复制代码

new Thread() { @Override public void run() { while(true); } }.start();

- 4.7资源未关闭:如果使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。
- 4.8集合容器:在我们做缓存的时候会用一些数据结构来存储一些数据,当我们不需要它时要及时清理,不然就会像滚雪球一样会越来越大,想不泄露都难。> 可以了,造成内存泄露还有很多原因,这就靠慢慢跳坑了,生活太艰难。再话痨一下,“千丈之堤,以蝼蚁之穴溃;百尺之室,以突隙之烟焚。”,所以我推荐套餐三Leakcanary,让你的整个开发过程伴随着内存泄露的监控。
复制代码

Android内存优化(二):一分钟发现内存泄漏相关推荐

  1. 【Android 内存优化】垃圾回收算法 ( 内存优化总结 | 常见的内存泄漏场景 | GC 算法 | 标记清除算法 | 复制算法 | 标记压缩算法 )

    文章目录 一. 内存优化总结 二. 常见的内存泄漏场景 三. 内存回收算法 四. 标记-清除算法 ( mark-sweep ) 五. 复制算法 六. 标记-压缩算法 一. 内存优化总结 内存泄漏原理 ...

  2. Android 内存优化实操,定位内存问题

    文章目录 一.内存泄漏定位 1.观察法: 2.使用内存分析工具 2-1.收集内存快照 2-2.hprof文件转换 2-3.Mat分析内存 二.内存抖动 三.优化内存空间 1.减少不必要的内存开销 2. ...

  3. Google官方 详解 Android 性能优化【史诗巨著之内存篇】

    尊重博主原创,如需转载,请附上本文链接http://blog.csdn.net/chivalrousman/article/details/51553114#t16 为什么关注性能 对于一款APP,用 ...

  4. Android系统性能优化(69)---含内存优化、布局优化

    Android性能优化:含内存优化.布局优化 前言 在 Android开发中,性能优化策略十分重要 因为其决定了应用程序的开发质量:可用性.流畅性.稳定性等,是提高用户留存率的关键 本文全面讲解性能优 ...

  5. java内存优化详解_jvm堆内存优化详解

    在日常的运维工作中用到tomcat,都需要对tomcat中的jvm虚拟机进行优化,只有知道需要优化参数的具体用处,才能深刻体会优化jvm的意义所在. 在平常的工作中我们谈对jvm的优化,主要是针对ja ...

  6. 内存优化-使用tcmalloc分析解决内存泄漏和内存暴涨问题

    其实我一直很想写关于tcmalloc的内存泄漏检测的文章,只是一直记不起来该如何下笔,有时项目太忙,在整理这方便的思考过少,另外遇到的问题也不是很多,直到最近用tcmalloc帮A项目排查一些很棘手的 ...

  7. 安全管家安卓_内存优化管家v1.0下载-内存优化管家app最新版下载

    内存优化管家是一款安卓手机多功能清理工具,包含了文件垃圾.缓存垃圾.广告垃圾和内存垃圾等分类清理功能,使用方法简单,一键扫描手机,即可进行不同类型的垃圾划分, 用户可以根据需求进行筛选清理,除此之外, ...

  8. redis持久化、内存优化、过期、LRU内存

    为什么80%的码农都做不了架构师?>>>    1.过期(expire命令) 设置了失效时间的元素,对于DEL/SET/GETSET/*STORE这些会删除或者重新设置元素的命令,如 ...

  9. 深入探索Android内存优化

    前言 成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~. 本篇是Android内存优化的进阶篇,难度会比较大,建议对内存优化不是非常熟悉的前仔细看看在 ...

最新文章

  1. 自动驾驶中,激光雷达点云如何做特征表达
  2. 统计寄存器AX中1 的个数
  3. 工业串口和网络软件通讯平台(SuperIO 2.1)更新发布
  4. Android Canvas和Paint
  5. android 判断空,Android空判断的坑
  6. 奇闻异事之NoSuchMethodError
  7. ipv6网络使用scp,并解决No route to host与no matches found报错
  8. PyTorch 1.0 中文官方教程:可选:数据并行处理
  9. 关于git stash的应用总结
  10. org manual翻译--3.6 Org-Plot
  11. 计算机系统配置有哪些,查看电脑配置方法有哪些
  12. Postgre SQL学习
  13. 【ubuntu如何录制gif图】
  14. JavaScript练字游戏
  15. 数据治理-数据质量管理
  16. 小学计算机二课活动记录,小学教研活动记录
  17. IO流总结(基础知识)
  18. 国产最强?室友用了直呼牛逼
  19. flask搜索引擎whoosh的配置
  20. 【Android初级】什么是ANR?如何防止?如何修复?

热门文章

  1. js与python 抓包_Python爬虫学习,记一次抓包获取js,从js函数中取数据的过程
  2. easyexcel和poi是否有版本冲突_easyexcel--解决poi大文件发生OOM问题
  3. caffe data层_Caffe Softmax层的实现原理?
  4. asp空间和php空间_免费ASP空间与免费PHP空间
  5. svm matlab 画图,matlab 画图
  6. dz后台html,discuz X1.5 论坛后台站长常用操作小结
  7. 4个空格 tab vetur_python学习的10个小技巧
  8. JS 日期转化及日期比较
  9. MyBatis框架学习笔记01:初探MyBatis实现简单查询
  10. 【NOIP2011TG】【OJ4366】【codevs1135】选择客栈