Android内存泄漏检测工具大全

来源:互联网 发布:动态恐怖漫画软件 编辑:程序博客网 时间:2024/06/06 12:35

什么是内存泄漏?

简单理解:没有被GC ROOT直接或者间接引用的对象的内存回收掉

性能优化就得考虑使用工具进行检测,Android关于内存工具很多,要能够定位剖析问题。但是会有写场景不会覆盖到,只能发现问题的能力工具。整合下基本使用到的工具。

  • top/procrank
  • STRICTMODE(楼主没有使用过)
  • MAT、Finder
  • meminfo
  • LeakCanary
  • LeakInspector
  • APT(腾讯开源:https://code.csdn.net/Tencent/apt/tree/master)
  • Allocation Tracker
  • Chrome Devtool
  • Systrace
  • Android Architecture Components

top/procrank

对于此工具来讲,需要知道几个名词含义:

  • VSS Virtual Set Size 虚集合大小
  • RSS Resident Set Size 常驻集合大小
  • PSS Proportional Set Size 比例集合大小
  • USS Unique Set Size 独占集合大小

    RSS 与 PSS 差不多,包含进程共享内存,RSS没把共享内存大小平分到使用共享的进程上,所以所有进程的RSS相加会超过物理内存。
    VSS是虚拟地址,它上线与进程的可访问地址有关,和当前进程的内存关系不大。
    PSS包含进程间共享的内存,而USS不包括,进程USS相加小于物理内存大小的原因。对于PSS而言,如果A进程和B进程同时共享同一个SO库,那就平分到了A和B上,但启动A进程,B没有启动,则B的PSS曲线图会有较大的阶梯状下滑。对于USS而言,它的坑在Dalvik申请内存会有GC延时及其策略,会影响曲线波动。

VSS >= RSS >= PSS >= USS

meminfo

使用dumpsys meminfo 命令
用法: dumpsys meminfo options:[-a][–oom][process]
-h帮助信息
-a打印所有进程的内存信息,以及当前设备的内存概况
–oom 按照OOM Adj值进行排序。
[Process]进程名,也可以是id
比如:dumpsys meminfo | grep -i phone,查 phone相关进程。
watch -n 5 dumpsys meminfo com.android.phone 每隔5秒刷新一次

Procsatats

要提到一个公式:内存负载 = PSS X 运行时长
内存负载在Android4.4上提出来(App在手机设备上显示一样),分为:前台,后台,缓冲负载。
前台,用户当前使用的,用户不关注的,内存负载不应该默认显示。
后台,是app行为,系统无权kill掉并回收,并非用户当前所使用,被默认展示。
缓冲,可以回收,内存负载不应该默认展示,没有被杀死是软件的恢复能力,是系统的责任。

DDMS

(Dalvik Debug Monitor Server)

各种调试信息集合,有时延,内存,线程,cpu等各种信息展示,经常使用Heap、Allocation Tracker 和 Dump Hprof file(内存快照)。

  • Update Heap获取GC信息,当前已分配内存,当前运行的对象,所剩内存,动态虚拟机heapSize,及其分布柱状图

  • Allocation Tracker , 展示了500条的内存分配,相关的线程堆栈信息。申请的内存大,GC就多,GC会挂起全部线程,会导致卡顿现象。

  • Dump Hprof file,选中进程,进行内存快照,Android Studio上有此功能。

MAT

全称:Memory Analyzer(内存分析),需要抓取Hprof文件,最简单使用ddms抓取,其次就是命令

抓取hprof命令如下(在adb shell模式下):

  • am dumpheap pid outfilePath (文件名必须为hprof)

操作:(DDMS抓取的可以直接忽略转格式问题)
1. adb shell am dumpheap com.android.phone /data/local/tmp/test.hprof
2. adb pull /data/local/tmp/test.hprof c:\test.hprof
3. hprof-conv c:\test.hprof c:\testConv.hprof

Finder

  • Activity 获取dump的所有Activity对象

  • Top Classes 对象数量或对象大小为维度来获取对象降序列表

  • Compare 对比两个Hprof文件内容的差别

  • Bitmap 获取dump的所有bitmap对象

  • Same Bytes 查询dump 中以byte[]类型出现并且内容重复的对象

  • Singleton 查询dump中的单例

能够准确定位80%的泄漏问题

LeakCanary

– 在onDestory 时检查弱引用,使用ReferenceQueue,白名单需要写死在AndroidExcludedRefs.java中。每次得重新编译,区分系统版本。

– 能识别系统泄漏,能够给出一个分析。

– 需要人工修复解决内存泄漏问题

– 使用开源组件HAHA 分析(参考简书),返回一条GC链。

LeakInspector

– 在onDestory 时检查弱引用,使用WeakReference,提供回调方法,能增加自定义的LOG,TRACE,DUMPSYS信息,需要在白名单以XML配置的形式存放到服务器上,不区分系统版本。

– 可以进行预处理,在ondestroy里,通过反射自动修复系统泄漏。

– 可以对对整个Activity的View遍历一遍,把图片所占用的内存数据释放掉,能够减少对内存的影响

– 采集dump后,自动通过Magnifier上传dump文件,调用MAT命令进行分析,返回GC链。

– 可以跟自动化测试无缝结合,自动化脚本执行过程中发现内存泄漏,收集dump发送到服务器上,分析,生成JSON结果。

JHat

Oracle公司开发的多人协作Hprof分析工具

使用命令:jhat test.prof

解析prof文件,开启httpSrv服务,维护解析后的数据,默认端口7000,直接访问查询。

libc_malloc_deBug_leak.so库

Android系统底层调用libc.so申请内存,而libc_malloc_deBug_leak库就是监视libc.so内部接口的调试库

Native Heap 就是类内存申请部分,NDK编译出来的so文件放到系统的 /data 目录中,size字段的值就是内存大小,每一个都拥有一个申请的调用栈,根据栈后的method字段值能够知道该方法的内存偏移,使用addr2line.exe转化方法名称,注需要编译选项中加入“-Wl,-Map=xxx,map -g”

APT

腾讯开源的测试工具,是DDMS的插件,能够实时监控多个app的cpu及其内存情况,以图表形式展示出来

参考官方

GC Log

Logcat输出的log日志,分为Dalvik Log和ART Log两种

  • Dalvik GC Log

GC 产生原因:

  1. GC_EXPLICIT : 通过Runtime.gc()与VMRuntime.gc(),SIGUSR1触发产生GC,支持并发GC,列表滑动,动画播放,不要有这种Log,高CPU低响应时延不要人工触发GC。

  2. GC_FOR_[M]ALLOC : 没有空闲内存空间给要分配的内存,不是并发GC,会有卡顿,尽量避免。

  3. GC_FOR_CONCURRENT : 当超过堆占用阀值时会触发,局部的并发GC 。

  4. GC_BEFORE_OOM : 在出发OOM前触发的GC,不能局部并发,耗时长,会卡顿。

  5. GC_HPROF_DUMP_HEAP : 在dump内存前触发GC,不能局部并发,耗时长,会卡顿。

查看工具:Dalvik GC Log 绘制成图表的工具

  • ART GC Log

GC 产生原因:

  1. GC_EXPLICIT : 通过Runtime.gc()与VMRuntime.gc(),SIGUSR1触发产生GC,支持并发GC,列表滑动,动画播放,不要有这种Log,高CPU低响应时延不要人工触发GC。

  2. GC_FOR_[M]ALLOC : 没有空闲内存空间给要分配的内存,不是并发GC,会有卡顿,尽量避免。

  3. GC_FOR_CONCURRENT : 当超过堆占用阀值时会触发,局部的并发GC 。

  4. NativeAlloc : Native内存不足以分配内存时触发。

  5. Background : 后台GC,给后面的内存申请预留空间。

GC 类型:

  1. Full : 跟Dalvik 的Full一样

  2. Partial: 跟Dalvik局部GC一样,没有Zygote Heap策略

  3. Sticky 局部中的局部GC ,上次垃圾回收后新分配的对象。

GC 的三种方式:

  1. mark sweep : 记录全部对象,从GC ROOT中找出直接或间接的对象做标注,利用之前记录的全部对象和标注对象做对比,剩余的便是要回收的。

  2. concurrent mark sweep : 使用mark sweep的并发GC

  3. mark compact : 对所有活动的对象压缩到内存的一侧,另外一侧进行回收。

  4. semispace : 把所有引用的对象从一个空间放到另外一个空间,剩余在旧空间的对象就是要直接GC掉的。

Allocation Tracker

Android Studio 里打开Monitor,选择要查看的进程,选择Allocation Tracker,录制出结果按照Size排序,能够直接通过dump to source跳转到对应代码行。

Chrome Devtool

需要安装Chrome浏览器,移动端和PC端,PC 端访问Chrome://inspect ,点击调试页面下的inspect,出来开发者工具

官方中文文档

Systrace

Android4.1上引入的性能分析工具,能够输出各个线程的当前函数调用状态,并且可以跟当前CPU的线程运行状态、VSYNC、SurfaceFlinger等系统信息在同一时间轴上进行对比。但不是所有手机机型都能支持。

SurfaceFlinger服务在每个VSYNC信号中断时调用一次,那APP显示非常流畅。

如果VSYNC的上升沿SurfaceFlinger服务没有调用,那会导致丢帧。
(1) CPU负载过大,低端机型的单核机型上会有发生。在VSNYC中断信号处,CPU闲暇,没有执行其他进程任务时,那我们需要做进一步分析
(2) 应用侧没有完成绘制,应用内部处于繁忙时,查看performTraversals信息,忙于其他业务逻辑没有绘制,那看是否忙于分发响应事件。如果当前窗口视图太多,布局嵌套太深,会导致查找响应输入事件的控件耗时长,没法绘制UI。

对于绘制等问题,Google添加了一些警告;
(1) Inefficient View alpha usage(5.1+以上才有)

(2) Expensive rendering with Canvas.saveLayer()

Canvas.saveLayer()会打断绘制过程中的渲染管道,替换使用View.LAYER_TYPE_HEARDWARE或者static Bitmaps,会让离屏缓存服用相邻两帧间的数据,避免渲染目标被切换而打断。

(3) Path Texture Churn

(4) Expensive Bitmap uploads

Bitmaps 在硬件加速下,修改和图像的变化都会上传给GPU,如果像素总量大,会消耗GPU的较大资源,所以建议减少每帧中对图片的修改,出现调用setLayerType为LAYER_TYPE_SOFTWARE之后,此时整个屏幕变成一张图。

(5) Inflation during ListView recycling

没用ListView复用,造成inflate的单个Item的getView成本比较高

(6) Inefficient ListView recycling/rebinding

每帧的ListView recycling 耗时较长,那得看下Adapter.getView()绑定数据的时候是否存在问题

(7) Expensive Measure/Layout pass

Measure / Layout 耗时导致卡顿,动画时,不要触发Layout

(8) Long View.draw()

Draw 本身耗时比较长,避免在View 或 Drawable 的onDraw里面执行任务频繁自定义操作,特别时申请内存和绘制Bitmap.

(9) Blocking Garbage Collection

GC导致卡顿,就是GC for Alloc的stop the world,避免在动画的时候生成对象,尽量重用Bitmap能够避免触发GC。

(10) Lock contention

UI 线程锁,UI线程去使用其他线程持有的锁,检查现有UI线程锁并确认它锁住的时间长短。

(11) Scheduling delay

网络I/O、磁盘I/O 等线程资源争抢,导致有一定时间的UI线程实际耗时长,而卡顿,检查后台线程是否都云溪nag在低优先级的线程上(是否比Thread_Priority_background还低)。

Android Architecture Components

Android 架构组件

存储数据,管理生命周期,模块化,避免常见错误,减少样板文件

Lifecycles

每个 Android 开发者都应该面对过生命周期问题,即操作系统启动、停止和销毁 Activity。这意味着开发者需要根据生命周期的不同阶段,有针对性地管理组件状态,比如用于更新用户界面的可观察对象。生命周期管理(Lifecycles)帮助开发者创建 “可感知生命周期的” 组件,让其自己管理自己的生命周期,从而 减少内存泄露 和崩溃的可能性。生命周期库是其他架构组件(如 LiveData)的基础。

LiveData

LiveData 是一款基于观察者模式的可感知生命周期的核心组件。一种可观测数据容器,它会在数据变化时通知观测器,以便更新界面。

LiveData 为界面代码 (Observer)的监视对象 (Observable),当 LiveData 所持有的数据改变时,它会通知相应的界面代码进行更新。同时,LiveData 持有界面代码 Lifecycle 的引用,这意味着它会在界面代码(LifecycleOwner)的生命周期处于 started 或 resumed 时作出相应更新,而在 LifecycleOwner 被销毁时停止更新。通过 LiveData,开发者可以方便地构建安全性更高、性能更好的高响应度用户界面。

有两个对应接口,LifecycleOwner和LifecycleObserver;
LifecycleOwner是具有生命周期的对象,比如Activity和Fragment.
LifecycleObserver观测LifecycleOwner,并在生命周期变化时收到通知。

ViewModel

ViewModel 将视图的数据和逻辑从具有生命周期特性的实体(如 Activity 和 Fragment)中剥离开来。直到关联的 Activity 或 Fragment 完全销毁时,ViewModel 才会随之消失,也就是说,即使在旋转屏幕导致 Fragment 被重新创建等事件中,视图数据依旧会被保留。ViewModels 不仅消除了常见的生命周期问题,而且可以帮助构建更为模块化、更方便测试的用户界面。

Room

一款简单好用的SQL对象映射库。它和 SQLite 有一样强大的功能,但是节省了很多重复编码的麻烦事儿。它的一些功能,如编译时的数据查询验证、内置迁移支持等,更简单地构建健壮的持久层。而且 Room 可以和 LiveData 集成在一起,提供可观测数据库并感知生命周期的对象。Room 集简洁、强大和可靠性为一身,在管理本地储存上表现卓越。

PagedList

解决用 RecyclerView 处理大数据集的困难。

App 架构指南
Android 架构组件官网


参考博客地址:

开源项目之LeakCanary源码分析: http://www.jianshu.com/p/5032c52c6b0a

查找并修复Android中的内存泄露—OutOfMemoryError:
https://yq.aliyun.com/articles/40294

内存分析工具 MAT 的使用 :
http://blog.csdn.net/aaa2832/article/details/19419679

Android 查看内存使用工具 (procstats):
http://blog.csdn.net/vshuang/article/details/51755756

Android进程内存统计工具procstats:
https://wenku.baidu.com/view/c8d49549b90d6c85ed3ac63b.html

原创粉丝点击