什么是内存泄漏?
简单理解:没有被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 产生原因:
GC_EXPLICIT : 通过Runtime.gc()与VMRuntime.gc(),SIGUSR1触发产生GC,支持并发GC,列表滑动,动画播放,不要有这种Log,高CPU低响应时延不要人工触发GC。
GC_FOR_[M]ALLOC : 没有空闲内存空间给要分配的内存,不是并发GC,会有卡顿,尽量避免。
GC_FOR_CONCURRENT : 当超过堆占用阀值时会触发,局部的并发GC 。
GC_BEFORE_OOM : 在出发OOM前触发的GC,不能局部并发,耗时长,会卡顿。
GC_HPROF_DUMP_HEAP : 在dump内存前触发GC,不能局部并发,耗时长,会卡顿。
查看工具:Dalvik GC Log 绘制成图表的工具
ART GC Log
GC 产生原因:
GC_EXPLICIT : 通过Runtime.gc()与VMRuntime.gc(),SIGUSR1触发产生GC,支持并发GC,列表滑动,动画播放,不要有这种Log,高CPU低响应时延不要人工触发GC。
GC_FOR_[M]ALLOC : 没有空闲内存空间给要分配的内存,不是并发GC,会有卡顿,尽量避免。
GC_FOR_CONCURRENT : 当超过堆占用阀值时会触发,局部的并发GC 。
NativeAlloc : Native内存不足以分配内存时触发。
Background : 后台GC,给后面的内存申请预留空间。
GC 类型:
Full : 跟Dalvik 的Full一样
Partial: 跟Dalvik局部GC一样,没有Zygote Heap策略
Sticky 局部中的局部GC ,上次垃圾回收后新分配的对象。
GC 的三种方式:
mark sweep : 记录全部对象,从GC ROOT中找出直接或间接的对象做标注,利用之前记录的全部对象和标注对象做对比,剩余的便是要回收的。
concurrent mark sweep : 使用mark sweep的并发GC
mark compact : 对所有活动的对象压缩到内存的一侧,另外一侧进行回收。
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
---------------------
作者:jink_l
来源:CSDN
原文:https://blog.csdn.net/superloveboy/article/details/78536750
版权声明:本文为博主原创文章,转载请附上博文链接!