如果说前两节对应用性能优化幅度有限的话,那么本篇内存则直接关系到应用的生死存亡。
好的优化可以让死亡边缘的应用起死回生,避免内存泄漏及OOM。
内存泄漏一般是长生命周期的对象持有短生命周期对象的引用,当短生命周期完成使命要被资源回收时,GC Root发现对象可达,所以并不回收,如果这样的情况发生很多,就容易造成内存浪费,严重时导致OOM。形象的说就好比,在餐厅吃饭,顾客点了一餐,实际上吃完了饭,但是手还端着碗没放开(持有碗的引用,占用内存), 服务员(GC)看到后认为其没吃完饭,所以本将收回碗筷结果就不收了,顾客吃完了,没吃饱,又点了一餐,吃完又没松手(之前的摞在一起),来回几次后,餐厅的碗不够用了,,,虽然不够准确,但也差不多是这个意思。

这里我们将使用多种手段揪出内存中的“病原体”, 涉及到 Android profiler Mem 和 Mat 以及 adb相关命令的使用。

一.Android profiler
Android Profiler网上教程太多了,包括官网也有详细介绍,常规的就不多说了,这里想给大家说下基于Android Profiler的内存优化的思路。

如图,Profiler的内存分析页面主要有两个功能按钮,一个是heap dump,一个是record,它们有什么区别呢?

Heap Dump有个官方的中文名叫堆转储(重要概念后面还会用到),不能指定时长,自动收集几秒的内存分配情况,保存了当前Java堆上所有的内存使用信息,能够完整的反映虚拟机当前的内存状态,并且还有内存泄漏的直接提示;它的文件格式是.hprof。

Record 用于记录内存分配,可以自由控制时长,但功能没有dump全面,不能直接查看内存泄漏。并且在Android 7.1以上版本时是没有这个按钮的,它的文件格式是.alloc。

我们关注 heap dump就好。
先使用dump快速查看内存的大体分配情况,以及是否有内存泄漏情况。
点击dump后生成如下视图(点击dump时会执行一次GC,内存也会稍微升高,这是正常现象)

可以看到,1处提示有14处内存泄漏的地方,我们在2处切换到”show activity/fragment Leaks”,查看页面导致的内存泄漏,3处显示了这些造成内存泄漏的fragment,选中第一个,在4处显示了它的所有实例,5处显示了它们的内存分配。
这里有4列,分别解释下它们的含义
Depth : 从任意 GC 根到选定实例的最短路径。
Native Size: 从 C 或 C++ 代码分配的对象的内存
Shallow Size: 对象本身占用内存的大小,不包含其引用的对象。这里可以看到6个实例它们的Shallow size都一样,因为创建fragment的动作都是一样的。
Retained size: 是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和(也可以理解为本身对象内存加上成员变量的内存)。换句话说,retained size是该对象被GC之后所能回收到内存的总和。这里用一个图来描述更为直观:

把内存中的对象看成图中的节点,并且对象和对象之间互相引用。这里有一个特殊的节点GC Roots,这就是reference chain的起点。从obj1入手,上图中蓝色节点代表仅仅只有通过obj1才能直接或间接访问的对象。因为可以通过GC Roots访问,所以左图的obj3不是蓝色节点;而在右图却是蓝色,因为它已经被包含在retained集合内。

所以对于左图,obj1的retained size是obj1、obj2、obj4的shallow size总和;右图的retained size是obj1、obj2、obj3、obj4的shallow size总和。obj1的Depth为1。
在左图中,obj2的retained size是obj2和obj4的shallow size的和;在右图中是obj2、obj3和obj4的shallow size的总和。Obj2的Depth为2。
清楚了基本知识后,我们继续往后看,点击6处的reference可查看所有引用当前NewsListFragment的对象。

随便选择一个,在红框处右键jump to source,定位到代码,如下:

代码跳转到了NewsListFragment的父类BaseFragment中的内部类SpaceItemDecoration,可以看到它是非静态的,在运行时会持有NewsListFragment的引用,我们将其改为static的,再运行应用重新dump,这个引用不存在了:

这里只是介绍解决问题的思路,并不是说这个SpaceItemDecoration就是内存泄漏的元凶,我这里的NewsListFragment是常驻的并不会销毁,只会隐藏和显示,所以即使有个多个不同的对象引用它也是没问题的,proflier这里提示leak,也只是从对象引用的角度来说,它并不知道我们的实际意图。所以仅做参考,并不是说有leak了就一定是问题,必须解决。当然如果你的Activity或者Fragment已经关闭了,在dump中还依然存在实例,那就是内存泄漏,需要解决。

这里放出官方对我们的建议:
在您的堆转储中,请注意由下列任意情况引起的内存泄漏:
1.长时间引用 Activity、Context、View、Drawable 和其他对象,可能会保持对 Activity 或 Context 容器的引用。
2.可以保持 Activity 实例的非静态内部类,如 Runnable。
3.对象保持时间比所需时间长的缓存

Tips
1.我们也可以通过命令导出.hprof文件。

adb shell am dumpheap pid /data/local/tmp/x.hprof

二.Android profiler + MAT
Dump的进阶使用方式,先记录一次,频繁操作一段时间后(可以使用monkey或者按键精灵或者其它自动化测试的工具,实现压力测试),再dump一次, 把两次的结果放到MAT中对比,从而清楚的了解到内存的变化情况。这种方式比只dump一次更加合理和直观。

点击保存按钮可以把内存信息保存为.hprof文件,这个文件需要转成MAT支持的格式(或者说标准的Java hprof格式,主要是版本号不一致),使用SDK/platform-tools里面hprof-conv.exe这个命令,如下:
hprof-conv old.hprof new.hprof
将第一次和第二次的.hprof文件都转换完成后,把这两个文件导入到MAT中(直接拖拽即可)。

Tips:MAT这里稍微科普下
MAT是Memory Analyzer的简称, 是基于Eclipse开发的(这个老Androider应该都用过吧)
官网:http://www.eclipse.org/mat/

导入时,选中Leak Suspects Report 再finish即可。

打开后,先选择after的那个hprof,然后选择 1 overview,2 Histogram(直方图)

再点击3处按钮和 before比较。


比较结果会直观列出Objects(对象数量) ,Shallow内存的变化情况,可以看到,after比before新增加了很多对象和内存。
(注意:跑monkey时尽量保证主Activity不会重建,否则会造成增长过多的假象,影响准确性)。

默认是以类的方式排列,还可以使用包名的方式排列:

这样就可以很直观的看到自己应用内存的变化了。
下面还有第二种比较方式,可以更全面友好的显示内存变化的情况,
在Histogram tab页下点击1处的Navigation history,然后在OverviewPane最后一个histogram上右键Add to Compare Basket

两个都添加过来后(before在上),然后点击红框处的红色叹号(Compare the results) 进行比较

比较结果如下,相比第一种比较方式,这种的列数更多,把before和after的数据都列了出来。

还可以切换其它的视图,比如用百分比显示变化的情况。

如果有增长特别多的类,那么有可能存在内存泄漏,可以选择with incoming references查看引用它的对象。

关于outgoing和incoming备注里还会介绍。
以上是自己手动对比内存的变化,如果你想偷懒,想快速查看是否存在内存泄漏的方法,MAT提供了一个名字很霸气的功能,叫做:
Leak Hunter(泄漏捕手)
两种方式打开:


打开后,长这样:

按泄漏的大小排序,查看其中一个问题的detail:

点击Refercenec Pattern里的类名-List objects-with incoming references 查看谁引用了它。(有的不一定有Refercenec Pattern)

然后在这里列表里查看详细的引用关系:

除了泄漏捕手,MAT还提供了大对象的查看方法,这个也是我们的优化方向。
在overview页面选择Top Consumers查看应用中的大对象并按照大小排序,以饼图的形式展示。


可以看到这个byte[]大对象里面包含了Glide和CircleImageView中的bitmap。
遗憾的是这里并不能像AS proflier中那样直接调转到代码,因为这里是脱离了项目的代码环境。不过也足够详细了,毕竟应用内所创建的java 对象这里都会一个不拉的显示出来:

展开CircleImageView后可以看到,personFragment中有个名为 mHeadIv的CircleImageView类型的对象,它占用的内存空间分别是 Shallow Heap 304字节 , Retained heap 2544字节。

好了,MAT的介绍就到此为止。仅仅只是抛砖引玉,MAT的强大远远不止这些,比如它支持OQL(Object Query Language),你可以查某个类的所有实例甚至是按地址搜索某个对象。

到这里,我们了解到了 4种分析内存的方法,总结如下:
1.单纯Android Profiler Mem: 最便利的方法,可以直接查看leaks情况,功能强大,可跳转到源码(首选)
2.MAT 对比.hprof文件: 操作稍微繁琐,但很直观,也更贴近真实场景
3.MAT leak hunter: 快速查看可能的内存泄漏(感兴趣的人可以和profiler中的leak对比下,看看有没有异同)
4.MAT top consumer: 快速查看大对象
可以说使用了Profiler 和MAT, 简直就是中西结合,药到病除。

三.adb命令
有人说了,前面介绍的这些方法都太麻烦,还有没有简单点的?我就想看下内存占用的情况。
当然有了,它就是adb命令(挺适合没有as的测试人员使用),本节介绍五种方式。

第一种:procrank
adb shell su (需要赋予超级权限,否则可能报错)
procrank -p (按pss排序)

VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

第二种:dumpsys
adb shell dumpsys meminfo 包名(后面不加包名则是查看所有的):

不仅能看应用的各项内存指标,还能看到创建了多少个view和activity(1处),甚至还能看到有哪些数据库,是不是很强大(2处)。

第三种:查看系统内存情况:
adb shell cat /proc/meminfo(一般看available即可)

第四种:showmap,可以查看每个so库占用的内存大小

adb shell showmap pid

第五种:smaps
查看进程的虚拟内存空间

adb shell run-as 包名 cat /proc/pid/smaps

参考:
https://www.cnblogs.com/bravery/archive/2012/06/27/2560611.html

adb总结: 我们介绍了5种查看内存的方式,分别是
Procrank、dumpsys 、proc、showmap、smaps(其实也都是基于Linux的),操作方便,一行命令即可,前四个适合普通测试人员使用。smaps入手门槛较高,适合进阶使用。以上命令在末尾处添加 > xxx.log 都可以把信息保存到文件中。

总结:
本章节我们主要介绍了应用稳定性的重中之重-内存,恼人的OOM和内存泄漏就常常是因为内存使用不当而产生(严格来说,造成oom的原因还可能有线程数量过大、Fd(文件描述符)数量过大、虚拟内存不足等),几乎是最影响应用稳定性的部分了。
我们的解决办法就是dump出hprof内存快照文件,通过两种工具AS和MAT进行分析得出结论,但是这部分只是针对于Java层的堆内存,除了这个还有native的内存,主要和so库相关,不能够直接通过现成的方法获取到相关信息,一般是通过hook系统库libc.so的malloc和free函数去获取到native层所有的内存的申请和释放操作,再结合smaps(进程虚拟内存信息)文件进行分析,将相对独立的内存信息追溯到业务堆栈从而定位到具体方法(这也是为什么bugly中的native异常需要提供so符号表才能定位的原因)。当然也有一些现成的工具,比如 HeapSnap,malloc_debug,asan,valgrind。
这部分涉及到 Linux动态链接等底层知识,我们没法过多的介绍。
感兴趣的小伙伴可以参考 腾讯的native内存分析与监控:
https://mp.weixin.qq.com/s/0cF5Q6_LXrkLAdjkXIwrVQ
以及西瓜视频的线上native内存的泄漏监控Raphael:
https://github.com/bytedance/memory-leak-detector

备注:
1.MAT中的引用关系
list objects – with outgoing references : 查看这个对象持有的外部对象引用(引用谁)。
list objects – with incoming references : 查看这个对象被哪些外部对象引用(被谁引用)。
show objects by class – with outgoing references :查看这个对象类型持有的外部对象引用
show objects by class – with incoming references :查看这个对象类型被哪些外部对象引用

2.三方分析Heaphero
如果懒得自己去分析内存问题,其实可以交给三方机构HeapHero(应该还有很多类似的),免费还不用注册,只需要提交dump文件即可(知道它为什么叫堆转储了吧,其实就是把瞬间的内存堆信息转化为可存储的持久化信息,有了这个堆信息就像是体检报告,你可以拿着到处问医生开处方了 )
是不是很方便?但前提是英语要过关,,,
https://heaphero.io/heap-index.jsp#header

3.Android 8.0之后 图片内存的申请由Java层切换到了native层

4.这里需要注意,物理内存不足时,会引起 onLowMemory 回调;当虚拟内存占用超过最大限制的 90% 时,触发为低内存告警。超过最大限制则直接触发 OOM,因此我们也需要监听虚拟内存的占用情况。

一些可供参考的文章:
MAT: Incoming Vs Outgoing References
https://cloud.tencent.com/developer/article/1530223

Java堆:Shallow Size和Retained Size
https://blog.csdn.net/kingzone_2008/article/details/9083327

内存分析诊断系列-理解heap dump
https://blog.csdn.net/u012811805/article/details/106547389

官方教程
https://developer.android.google.cn/studio/profile
https://developer.android.google.cn/studio/command-line/dumpsys

线下场景-内存(Android profiler + MAT + adb)相关推荐

  1. 深度 | 线下场景的客流数字化探索与应用

    数字化的时代,无论是商场里的大小专柜,还是小区门口的便利店,大多仍处于"数据荒漠"中.店家不知道店内多少商品被人浏览,多少衣服被试穿了,作为顾客的我们也不知道哪些商品是最受同龄人喜 ...

  2. 第一家线下场景大数据平台Anchor-Point诞生

    近日,由衍合数据主办的"<Anchor-Point线下场景大数据分析平台发布会"在上海隆重举行.近80位媒体广告行业和数据研究龙头齐聚一堂,见证中国第一家线下场景大数据平台的 ...

  3. 起底滴滴数据科学团队:面对超复杂线下场景,要数据驱动,但拒绝“唯数据论”...

    来源:大数据文摘 本文约4300字,建议阅读9分钟. 本文为清华大学大数据研究中心联合大数据文摘发起的年度白皮书<顶级数据团队建设全景报告>系列专访的第一篇内容. <报告>囊括 ...

  4. 【寻找最佳小程序】04期 :探访“小打卡”产品打磨细节及线下场景真实应用

    专栏记者:陈秋歌 如果您希望将本人或团队开发的小程序介绍给更多人了解.使用,十分欢迎联系本专栏记者陈秋歌(chenqg#csdn.net.微信:Rachel_qg),获得CSDN的采访报道及宣传推广机 ...

  5. 【寻找最佳小程序】04期 :探访“小打卡”产品打磨细节及线下场景真实应用...

    专栏记者:陈秋歌 如果您希望将本人或团队开发的小程序介绍给更多人了解.使用,十分欢迎联系本专栏记者陈秋歌(chenqg#csdn.net.微信:Rachel_qg),获得CSDN的采访报道及宣传推广机 ...

  6. Linux下ion内存,android内存管理-ION/PMEM

    ION debug ION 在/sys/kernel/debug/ion/ 提供一个debugfs 接口. 每个heap都有自己的debugfs目录,client内存使用状况显示在/sys/kerne ...

  7. 从支付宝「蜻蜓」布局看线下支付场景的想象空间...

    4月17日,支付宝发布第二代基于线下消费场景的刷脸支付机具「蜻蜓」.和去年12月推出的首代「蜻蜓」相比,二代「蜻蜓」在价格和外观方面取得了显著提升.(详情请查阅文末雷锋网报道:未来三年投30亿,支付宝 ...

  8. 从支付宝「蜻蜓」布局看线下支付场景的想象空间

    4月17日,支付宝发布第二代基于线下消费场景的刷脸支付机具「蜻蜓」.和去年12月推出的首代「蜻蜓」相比,二代「蜻蜓」在价格和外观方面取得了显著提升.(详情请查阅文末雷锋网报道:未来三年投30亿,支付宝 ...

  9. 「深度」线下大数据正成为构建精准“用户画像”的最大助力

    不管是针对消费者的宣传还是营销,或者是针对公司的管理运营,大数据在其中的作用从本质来讲就是在构造"用户画像". 近年来,在智能化趋势的推动下,社会经济的众多领域都发生了翻天覆地的变 ...

最新文章

  1. 文巾解题 20有效的括号
  2. 【渝粤教育】广东开放大学 计量经济学 形成性考核 (21)
  3. 【转载】Windwos CE 跨进程内存注入
  4. 如何运行python代码
  5. Android 呼吸灯流程分析
  6. html表格中加横线,表格下划线怎么打出来
  7. whitepages 被调查_槐荫区美里湖办事处残联开展2020年残疾人精准服务需求调查工作...
  8. hdu 1054 Strategic Game 树形dp基础模板
  9. 关于iOS实现前台,后台,锁屏或关闭app语音播报
  10. 程序员必备网页前端设计网站
  11. CUDA库之NPP(四):内存开辟和字节对齐
  12. 简单粗暴日文键盘布局改为其他语言键盘布局
  13. tcr历史比赛竞赛规则
  14. HTML_canvas
  15. PTA 1107 老鼠爱大米(C++实现)
  16. EF core和数据库, Database First
  17. 企业级别的应用程序开发
  18. vb 与 .accdb 格式的access数据库的连接方法
  19. TikTok带货爆单:彩色人造玫瑰周增长率飙升,TikTok热卖榜前10?
  20. win7系统怎么调亮度_win7系统怎么打开vsd文件 win7系统打开vsd文件步骤【图文】...

热门文章

  1. 【基操】添加环境变量
  2. 最新js文件打开方式
  3. 庖丁解牛-正则表达式
  4. 小米商城项目(springboot+thymeleaf)
  5. 集成存储器控制器(IMC)功能
  6. Oracle中decode、to_char、rollup函数用法
  7. 【转】VMware Workstation 11 永久激活码key 非注册机
  8. element-ui 布局拆分组件,布局出现问题
  9. 微信授权莫名创建用户数据失败的原因
  10. 【机器学习|数学基础】Mathematics for Machine Learning系列之矩阵理论(1):集合与映射