什么是内存泄漏

对于不同的语言平台来说,进行标记回收内存的算法是不一样的,像 Android(Java)则采用 GC-Root 的标记回收算法。下面这张图就展示了 Android 内存的回收管理策略(图来自Google 2011的IO大会)

图中的每个圆节点代表对象的内存资源,箭头代表可达路径。当圆节点与 GC Roots 存在可达路径时,表示当前资源正被引用,虚拟机是无法对其进行回收的(如图中的黄色节点)。反过来,如果圆节点与 GC Roots 不存在可达路径,则意味着这块对象的内存资源不再被程序引用,系统虚拟机可以在 GC 过程中将其回收掉。

有了上面的内存回收的栗子,那么接下来就可以说说什么是内存泄漏了。从定义上讲,Android(Java)平台的内存泄漏是指 没有用的对象资源任与GC-Root保持可达路径,导致系统无法进行回收。 举一个最简单的栗子,我们在 Activity 的 onCreate 函数中注册一个广播接收者,但是在 onDestory 函数中并没有执行反注册,当 Activity 被 finish 掉时,Activity 对象已经走完了自身的生命周期,应该被资源回收释放掉,但由于没有反注册, 此时 Activity 和 GC-Root 间任然有可达路径存在,导致 Activity 虽然被销毁,但是所占用的内存资源却无法被回收掉。类似的栗子其实有很多,不一一例举了。对于 Android(Java)内存回收管理想要再深入了解的童鞋,可以看看下面资源:

可以作为GC Roots的对象包括下面几种:

  • 虚拟机栈中引用的对象, 一般是当前在使用中局部变量

  • 方法区中类静态属性引用的对象, 就是静态变量对应的对象

  • 方法区中常量引用的对象

  • 本地方法栈中JNI(即一般说的Native方法)引用的对象

MAT分析内存泄漏的时候,也是查看对象到GC Roots的引用链,来定位泄漏代码的位置。

所以未使用的对象直接或间接地被GC Roots引用时会让GC无法回收,从而产生内存泄漏。

Android 可能引起泄露的例子请参考

http://www.cnblogs.com/daqiang5566/p/6150235.html

  • 静态代码分析工具 —— Lint

Lint 是 Android Studio 自带的工具,使用姿势很简单 Analyze -> Inspect Code 然后选择想要扫面的区域即可

对可能引起泄漏的编码,Lint 都会进行温馨提示。

这里只是抛砖引玉的介绍 Lint ,实际上玩法还有很多,大家可以自行拓展学习。除了 Lint 外,还有像 FindBugs 、 Checkstyle 等静态代码分析工具也是很不错的。

  • 严苛模式 —— StrictMode

StrictMode有多种不同的策略,每一种策略又有不同的规则,当开发者违背某个规则时,每个策略都有不同的方法去显示提醒用户,目前,有两大类的策略可供使用。

(1)是关于常用的监控方面的,

      Disk Reads 磁盘读

  Disk Writes 磁盘写

  Network access 网络访问

  Custom Slow Code 自定义的运行速度慢的代码分析

     分别对磁盘的读和写,网络访问进行监控。而第四种的自定义慢代码分析,是仅当访问调用类的时后才触发的,可以通过这种方法去监视运行缓慢的代码。当在主线程中调用时,这些验证规则就会起作用去检查你的代码。比如,当你的应用在下载或者解析大量的数 据时,你可以触发自定义运行速度慢代码的查询分析,作用很大。StrictMode可以用于捕捉发生在应用程序主线程中耗时的磁盘、网络访问或函数调用,可以帮助开发者使其改进程序,使主线程处理UI和动画在磁盘读写和网络操作时变得更平滑,避免主线程被阻塞的 发生。

(2)另外一类是关于VM虚拟机等方面的策略

     内存泄露的Activity对象

   内存泄露的SQLite对象

   内存泄露的未释放的对象

     当开发者违反某类规则时,每种策略都会有不同的方法令开发者知道当时的情况。相关的违反情况可以记录在LogCat中或者存储在DropBox中(android.os.DropBox)服务中。而常用监控类的策略还会在当违规情况发生时显示相关的对话框和当时的上下文环境,所有的这些都为了能让开发者尽快地了解程序的瑕疵,以提交程序的质量

StrictMode是Android系统提供的API,在开发环境下引入可以更早的暴露发现问题。官方文档链接在下面(需要科学上网)。

https://developer.android.com/reference/android/os/StrictMode.html

以官网的示例代码为栗子,一般 StrictMode 只在测试环境下启用,到了生产环境就会进行关闭,通常我们都会借助 BuildConfig.DEBUG 来实现。

启用 StrictMode 后,在过滤日志的地方加上 StrictMode 的过滤 Tag ,如果手机连接着电脑进行开发,定期观察一下 StrictMode 这个 Tag 下的日志,一般你看到一大堆红色告警的 Log.就需要好好排查一下是否跟内存泄漏有关了。

详细的StrictMode 请参考:http://www.cnblogs.com/daqiang5566/p/6163756.html

  • Android Memory Monitor

AndroidStudio 提供的工具,用于监控应用的内存使用状态,在开发中也是非常实用的工具,可以用来打印出内存的状态信息。

打印获得的内存信息如下,可以通过右上角的绿色三角形按钮去分析泄漏的 Activity 和 一些重复的字符串,目前只支持这两个,希望 Google 后面能够加入更多可选分析规则

同样,这里也只是抛砖引玉的简单介绍,关于它的使用在官方文档已经说得很详细了,需要的童鞋自行查看下方链接(需科学上网):

https://developer.android.com/studio/profile/am-hprof.html

  • Memory Analyzer (MAT)

老牌子分析工具,可以从 http://www.eclipse.org/mat/ 下载获得,网上关于 MAT 使用的文章好多,大家可以自行查找。上面的 Android Memory Monitor 生成的对储存信息文件可以配置 MAT 一起来分析使用,由于 Android Memory Monitor 生成的 hprof 文件不是标准格式,所以需要做一下转换,然后导入 MAT

然后通过 OQL 先定位出泄漏的对象

通过排除除了强引用之外的其他引用链,最后分析到 GC Root 的位置

MAT 使用起来相对繁琐,但不失为定位根源问题的利器。

  • adb shell 命令

使用 adb shell dumpsys meminfo [PackageName],可以打印出指定包名的应用内存信息

使用该命令可以很直观的观察到 Activity 的泄漏问题,是我平常分析比较常用的一种方式。除了使用命令外,AndroidStudio 也提供了下面的功能,和使用命令是一样效果的。

如果对 adb shell 命令感兴趣,更多的信息可以看下面提供的资源:

以上就是我在做内存泄漏分析的时候会用到的工具,通常都是结合起来用,毕竟每个工具都有优缺点,通过使用多个工具互补分析问题可以极大的提高我们的效率和最终取得的效果。

内存泄露工具:leakcanary 也可以检车内存泄露问题,可自行百度

泄漏的解决策略

(1) 注意Activity的泄漏

  • 内部类引用导致Activity泄漏
          http://www.cnblogs.com/daqiang5566/p/6150235.html
  • Activity Context被间接引用

对于大部分非必须使用Activity Context的情况(Dialog的Context就必须是Activity Context),我们都可以考虑使用Application Context而不是Activity的Context,这样可以避免不经意的Activity泄露

(2) 注意静态变量和单例模式

静态变量是作为GC Roots,在Android其生命周期基本和进程一样长,所以要非常静态变量引用其他生命周期的对象。虽然单例模式简单实用,提供了很多便利性,但是因为单例的生命周期和应用保持一致,使用不合理很容易出现持有对象的泄漏。

(3) 注意容器中对象泄漏

有时候,我们为了提高对象的复用性把某些对象放到缓存容器中,可是如果这些对象没有及时从容器中清除,也是有可能导致内存泄漏的。例如,针对2.3的系统,如果把drawable添加到缓存容器,因为drawable与View的强应用,很容易导致activity发生泄漏。而从4.0开始,就不存在这个问题。解决这个问题,需要对2.3系统上的缓存drawable做特殊封装,处理引用解绑的问题,避免泄漏的情况。

(4) 注意监听器的注销

(5) …

(6) 及时关闭Cursor

在程序中我们经常会进行查询数据库的操作,但时常会存在不小心使用Cursor之后没有及时关闭的情况。这些Cursor的泄露,反复多次出现的话会对内存管理产生很大的负面影响,我们需要谨记对Cursor对象的及时关闭。

  • 深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)

  • Google IO 2011 Memory management for Android Apps

泄漏的源头

了解完内存泄漏的理论知识后,再来归类一下内存泄漏的源头。这里我将其归位以下三类:

  • 自身编码引起

由项目开发人员自身的编码造成。

  • 第三方代码引起

这里的第三方代码包含两类:第三方非开源的SDK和开源的第三方框架。

  • 系统原因

由 Android 系统自身造成的泄漏,如像 WebView 、 InputMethodManager 等引起的问题,还有某些第三方 ROM 存在的问题。

泄漏的定位

内存泄漏不像闪退的BUG,排查起来相对要比较困难些,比较极端的情况是当你的应用 OOM 了才发现存在内存泄漏问题,到了这种情况才去排查处理问题的话,对用户的影响就太大了。为此,我们能够在编码中尽早发现到问题就不要拖到上线之后才去填坑,下面介绍一些我比较常用排查内存泄漏的工具。

    静态代码分析工具 —— 火线(360出品)  

参考地址:http://magic.360.cn/index.html

1. APP安全检查
     2. 代码规范检查
     3. 内存泄露检查
     4. 日志输出检查
     5. 空指针检查
     6. 多线程检查

转自:http://mp.weixin.qq.com/s?__biz=MzA4MzEwOTkyMQ==&mid=2667376765&idx=1&sn=e738d79cb50af147b4f67d9d368bc5a2&chksm=84f32133b384a825113e6e32fb54e9c27192bfa2a6cd09d3eb74912515037ca6af11a88a3791&scene=0#rd

Android应用内存泄漏的定位、分析与解决策略的更多相关文章

  1. java内存泄漏的定位与分析

    1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题. 编写java程序最为方便的地方就是我们不需要管理内存的分配和释放, ...

  2. (转)java内存泄漏的定位与分析

    转自:http://blog.csdn.net/x_i_y_u_e/article/details/51137492 1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测, ...

  3. 利用Android Studio、MAT对Android进行内存泄漏检测

    利用Android Studio.MAT对Android进行内存泄漏检测 Android开发中难免会遇到各种内存泄漏,如果不及时发现处理,会导致出现内存越用越大,可能会因为内存泄漏导致出现各种奇怪的c ...

  4. Android Native 内存泄漏系统化解决方案

    导读:C++内存泄漏问题的分析.定位一直是Android平台上困扰开发人员的难题.因为地图渲染.导航等核心功能对性能要求很高,高德地图APP中存在大量的C++代码.解决这个问题对于产品质量尤为重要和关 ...

  5. Android防止内存泄漏以及MAT的使用

    Android发生内存泄漏最普遍的一种情况就是长期保持对Context,特别是Activity的引用,使得Activity无法被销毁.这也就意味着Activity中所有的成员变量也没办法销毁.本文仅介 ...

  6. android性能测试内存泄漏

    1.什么是内存泄漏?     适用于该系统的内存使用内存泄漏,未回复(释放),该内存可以没有事业,也不能被其他人使用使用自己. 2.出有什么差别?    内存泄漏是分配出去的内存无法回收.    内存 ...

  7. android 常见内存泄漏原因及解决办法

    android常见内存泄漏主要有以下几类: 一.Handler 引起的内存泄漏. 在Android开发中,我们经常会使用Handler来控制主线程UI程序的界面变化,使用非常简单方便,但是稍不注意,很 ...

  8. 一个驱动导致的内存泄漏问题的分析过程(meminfo->pmap->slabtop->alloc_calls)

    关键词:sqllite.meminfo.slabinfo.alloc_calls.nand.SUnreclaim等等. 下面记录一个由于驱动导致的内存泄漏问题分析过程. 首先介绍问题背景,在一款嵌入式 ...

  9. [原理] Android Native内存泄漏检测原理解析

    转载请注明出处:https://www.cnblogs.com/zzcperf/articles/11615655.html 上一篇文章列举了不同版本Android OS内存泄漏的检测操作(传送门), ...

随机推荐

  1. html input节点很多 json字符串提交解决方法

    遇到一个页面,38个input节点,页面前端写好的,不太容易改成 js框架 容易操作的样式,只能自己想办法一个一个id获取然后 setvalue getvalue(miniui): 38个一个一个写太 ...

  2. JavaWeb--Servlet部分笔记

    1.集群:数万个服务器协同工作 2.web应用核心组件:jsp和servlet(属于门户),都在web容器中执行 3.web客户端发http请求(大的字符串)给web服务器:web服务器根据头信息来定 ...

  3. coding题都在考察我们的什么技能

    其实不是都在考我们的算法设计,有的时候算法简单,但是coding却可能不那么简单,比如下面这道题目: 题:写一个把字符串的IP地址变成32位整数的函数,要求考察程序健壮性. 这题初看起来一点都不难,但 ...

  4. Session阻塞 读写锁引发的小问题

      引子     我们来看两个ajax方法,先后的问题,会有什么样的结果? Javascript: $(function () { //第一个 $.ajax({ type: "POST&qu ...

  5. Mac下手动安装SafariDriver extension

    环境:Mac OS X Yosemite 10.10.4下, Safari 8 Step 1:第一次运行SafariDriver时,先找到WebDriver extension的安装路径,比如/Use ...

  6. 使用Excel 2007绘制甘特图

    本文将教大家如何使用Excel 2007制作甘特图.Excel并未提供甘特图类型,但还是可以绘制甘特图的,方法就是通过对堆积条形图类型进行自定义,使之显示任务.任务工期和层次结构. 下面的过程可帮助创 ...

  7. The next day to learn English

    if you smail when no  one else is around,you really meat it.

  8. linux命令:目录结构

      可分享的(shareable) 不可分享的(unshareable) 不变的(static) /usr (软件放置处) /etc (配置文件) /opt (第三方协力软件) /boot (开机与核 ...

  9. 缓存和sd卡的路径(原)

    在需要存储的时候,路径的问题是初学者比较迷惑的,下面是对于getCacheDir().getFilesDir().getExternalFilesDir().getExternalCacheDir() ...

  10. Android模拟器Genymotion如何访问本地服务器?

    Genymotion能否访问到本地服务器,其实与Genymotion本身并无太大关系.Genymotion作为VirtualBox中的一个虚拟OS运行,所以它访问网络的方式与其他VirtualBox中 ...