2024年鸿蒙最新记录一次开机内存分析的全过程(3),鸿蒙算法面试题
https://i-blog.csdnimg.cn/blog_migrate/6e22e92452a7549faed0d22c8bf11145.pnghttps://i-blog.csdnimg.cn/blog_migrate/549e392588114589ebc3c2c169d807f4.png
网上学习资料一大堆,但假如学到的知识不成体系,遇到题目时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份体系化的资料的朋侪,可以戳这里获取
一个人可以走的很快,但一群人才气走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交换、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习发展!
利用BootRemainMemory需要注意几个点:
[*]手动覆盖安装应用和直接内置体系的版本的开机内存是有差异的,这里据同事说是由于两种方式对内存分区有不同的影响,因此我们假如没有条件每次都刷内置版本只能覆盖安装,那着重关注对比增量部分即可。
[*]假如设置间隔时间过短也会导致测试数据波动过大,因此这一步最好按照默认时间测试,这也导致比较耗时。
1.2 测试效果
带业务X均值46.5
不带业务X均值43.4
效果:开机内存确实有3MB左右的增长
这里我有个经验就是在跟测试对任何性能题目之前,必须首先确认这几项内容避免浪费时间:测试工具+测试手法+测试包+测试包的运行情况
2.排查是否引入带自启动功能的相干组件
我首先怀疑是否由于新增了自启动功能的组件导致内存新增,因此这里先选择排查内容提供器和静态广播。
2.1 ContentProvider
通过此前文章分析ContentProvider的启动特性+此前的Provider优化经验,我们知道Provider的启动时机是跟随应用自启动,而且Provider的onCreate方法执行早于Application的onCreate,这就很轻易带来开机内存的增长。
https://i-blog.csdnimg.cn/blog_migrate/290976139376016774b4a2bc3c32bfc0.png
因此首先怀疑是业务X有自启动的Provider引入了启动逻辑导致这部分内存增长,因此我们需要查找增量ContentProvider。
2.1.1 方法1
通过解析apk中归并后的AndroidManifest文件,搜索provider标签声明的组件,发现由贸易化广告sdk引入一个ContentProvider
2.1.2 方法2
这里除了通过查看Manifest文件以外,还可以通过"dumpsys package +包名"的方式进行查看已声明的组件。
2.1.3 方法3
利用MAT的OQL查询语句查询全部Provider实例,此方法比Profiler更准确,由于假如单纯利用Profiler的搜索功能无法辨认名称不包含Provider的实例。
SELECT * FROM INSTANCEOF android.content.ContentProvider
https://i-blog.csdnimg.cn/blog_migrate/e51dbc8edf57c148e0bf5bf1e6f4ac86.png
2.1.3 排查结论
再结合Profiler查看确实有一个ProcessProvider在开机阶段被加载。
https://i-blog.csdnimg.cn/blog_migrate/f15db3f98697f63959f051bad58997b9.png
进一步需要排查该Provider的具体作用和对开机内存的影响范围,进一步咨询广告同事,通过排查这个ContentProvider中生命周期onCreate中是否有非常内存利用情况。
确认结论为:现在初始化基本不涉及业务内存占用。
2.2 BroadcastReceiver
静态广播在开机也会立即被注册,因此还需要排查是否有静态广告在开机后被初始化带动初始化了相干逻辑。
假如我们在用户未同意申明权限的情况 下,是不应该响应吸收的功能的,这样也会间接引起内存的增长。
静态广播导致的开机内存增长的例子在我们项目中此前也出现过
因此我们同样利用上面Provider的排查方法发现也没有对应的Receiver起来,排除这个缘故起因的大概性。
3.利用Profiler定向排查怀疑的包名和类
当有确定的怀疑目的的时间,直接利用AS的Profiler进行搜索会服从会更高,因此我搜索了我能想到的业务X的类,只找到了DI注入相干的必须类,没有相干业务加载。
https://i-blog.csdnimg.cn/blog_migrate/7ae40351d9b5f863c831bf255ad18c69.png
3.1 Profiler底子
内存分析器是 Android Profiler 中的其中一个组件,可资助辨认大概会导致应用卡顿、冻结甚至崩溃的内存走漏和内存抖动。它表现一个应用内存利用量的实时图表,可以捕获堆转储、逼迫执行垃圾回收以及跟踪内存分配。
这里只介绍怎样利用Profiler查看内存堆转储文件的一些技巧,利用Profiler抓取内存文件大概获取hprof文件利用Profiler打开后就是以下界面。
https://i-blog.csdnimg.cn/blog_migrate/23ce5f177c7c987d4ac5cc5b4ef25df6.png
4.利用Mat对比Heap变化
利用Profiler分析照旧有一定局限,只能对已知怀疑的相干类进行逐一排查,对未知的类引入我还不是很确定,因此我进一步抓取了两份hprof利用MAT进行对比分析,试图找出具体的增量缘故起因。
得当利用mat分析的场景包括:需要对比某个举动前后的内存,需要对比两个不同版本的内存等。
4.1 Mat底子和分析技巧
4.1.1 Histogram
MAT中Histogram的紧张作用是查看一个instance的数目,一样平常用来查看自己创建的类的实例的个数,通过查看Object的个数,结合代码就可以找出存在内存泄露的类,Histogram中还可以对对象进行Group,更方便查看自己Package中的对象信息
https://i-blog.csdnimg.cn/blog_migrate/5fc6b4e7c0216d0439e1a26b685edb0a.png
4.1.2 Dominator Tree
可以很轻易的找出占用内存最多的几个对象,根据Percentage(百分比)来排序
Dominator Tree和Histogram的区别是站的角度不一样,Histogram是站在类的角度上去看,Dominator Tree是站的对象实例的角度上看,Dominator Tree可以更方便的看出其引用关系。
https://i-blog.csdnimg.cn/blog_migrate/5c4919d55460d35473d114f94ff16012.png
4.1.3 List objects -> with incoming references/with outcoming references
查看这个对象持有的外部对象引用/查看这个对象被哪些外部对象引用
4.1.4 Path To GC Roots -> exclude all phantim/weak/soft etc. references:
查看这个对象的GC Root,不包含虚、弱引用、软引用,剩下的就是强引用,通常用于查看内存走漏的根引用关系,另有其他几种模式就不一一解释了。
https://i-blog.csdnimg.cn/blog_migrate/5a95f8351897455ff8b67d7bbb2296e6.png
4.1.5 OQL语句
SELECT * FROM com.oplus.assistantscreen.window.AssistantWindowBlurSlideView //查询某个具体类
SELECT * FROM INSTANCEOF android.content.ContentProvider // 查询全部的的Provider以及其子类的实例
SELECT * FROM INSTANCEOF java.lang.Exception exceptions// 查询全部的非常对象以及其子类的实例
https://i-blog.csdnimg.cn/blog_migrate/dbe50ad25171b6ee0de5938638b7ce05.png
SELECT * FROM java.lang.String s WHERE s.count >= 100 //查询长度超过 100的字符串
https://i-blog.csdnimg.cn/blog_migrate/de2ef4f50a36fd36d8cd099177163e09.png
SELECT * FROM INSTANCEOF java.util.HashMap s WHERE s.size>10//查询长度超过10的HashMap
https://i-blog.csdnimg.cn/blog_migrate/5ed9400538ed47f8666362d97ec35919.png
4.2 hprof命令行抓取方法
抓取hprof有一定条件,假如整机是root的版本,那不管是release的照旧debug的都能抓,假如好坏root的版本,则需要是debug的历程才气抓。
hprof 文件可以在代码中进行 dump,也可以用 Android Studio进行 dump , 也可以利用其他第三方工具进行 dump。
这里我介绍另外一种利用命令行dump的方式,先查询历程号-再利用dumpheap进行抓取-末了导出文件即可。
D:\Users\80343288>adb shell
OP5267:/ # ps -ef|grep assis
u0_a237 4907 833 2 14:27:29 ? 00:00:00 com.xx.xx
u0_a237 5530 833 12 14:27:33 ? 00:00:03 com.xx.xx:xx
root 8663 7044 15 14:27:55 pts/1 00:00:00 grep assis
OP5267:/ # am dumpheap -g 4907 /data/local/tmp/heapdump_noinfo.hprof
File: /data/local/tmp/heapdump_noinfo.hprof
Waiting for dump to finish…
OP5267:/ # exit
D:\Users\80343288>adb pull /data/local/tmp/heapdump_noinfo.hprof
/data/local/tmp/heapdump_noinfo.hprof: 1 file pulled, 0 skipped. 30.5 MB/s (36076684 bytes in 1.130s)
一样平常利用AS大概命令行抓取的hprof文件要导入Mat之前,需要利用hprof-conv工具进行转换格式
首先辈入sdk自带的hprof-conv工具目次(D:\Software\Android Studio Others\win-sdk\platform-tools目次)
执行如下命令:
hprof-conv -z memory-20200625T145636.hprof mat.hprof
4.3 利用Mat对比
利用mat分别导入需要对比的两份hprof文件,然后选择对比
特别注意要对比的话不要利用release版本的hprof文件,由于每次混淆后的类名是不一致的,会导致对比可读性很低。
https://i-blog.csdnimg.cn/blog_migrate/37ae957693cab8814c704941b3e0ec59.png
https://i-blog.csdnimg.cn/blog_migrate/3eeeedc696938d5ebb6b7c42e03b4f55.png
4.4 排查效果
从这里的对比效果可以看到没有明显的对象分配数目非常,由于hprof只能观察Java堆内存的分配情况,因此增长应该来自于其他,需要抓取更完整的内存文件进行分析。
注意:虽然我们这个题目在这里没有得到结论,但是一样平常情况下,要是有内存走漏大概是大内存的分配非常,通过这样对比两份内存文件再排序,就会很直观的暴露出来,这是很有效的办法。
5.抓取meminfo查看团体内存状况
5.1 meminfo底子和分析技巧
利用meminfo可以查看指定历程或包名的内存团体利用情况,一样平常查看这个文件后可以确定是哪个内存大块出题目,而且这个内存信息相对来说获取成本较低,是我们分析内存最常用的方式。
meminfo的数据来源是解析smaps等信息分类汇总而成,数据单元KB,具体原理见背面的参考文档。
5.1.1 meminfo底子解读
https://i-blog.csdnimg.cn/blog_migrate/08cd1e6868c5b488e11c0ecdf22b18b2.png
5.1.2 常用分析思绪
分析思绪一样平常是抓取两份meminfo进行对比查看,根据对比效果,按照以下路径进一步分析是哪一部分的题目
[*] 假如是 Dalvik 部分内存变大,需要去查看 hprof 文件
[*] 假如是 Native 部分内存变大,需要去根据 Native Debug 的文档,共同 hprof 文件进行分析,大部分 App 的 Native 内存变大都是 Java 层的调用导致的
[*] 假如是Graphics增大,则查看具体是 GL mtrack / EGL mtrack
[*] 需要查看 gfxinfo 的效果
[*] 需要对比两台呆板的分辨率、App 的 SurfaceView 、TextureView、Webview 等利用情况
[*] 需要查看 App 硬件加速的利用情况
[*] 假如是 so / jar / apk / ttf 变大,进一步获取smaps查看 so / jar / apk / ttf 的个数,对比查看是哪部分变大,大概是由于多了哪个 so / jar / apk / ttf 导致的
[*] 假如是 dex/oat/art 变大,则需要对比两个 app 的运行状态、应用版本号是否一致,由于这部分与 Android 运行时的关系比较大, 需要利用 user 版本进行测试
5.2 抓取meminfo
该命令行无需手机root,无需一定要debug版本的应用
注意:一样平常可以多dump几次以末了一次为准,由于每次执行会默认触发一次逼迫GC
D:\Users\80343288\mem_tmp>adb shell dumpsys meminfo com.xx.xx> meminfo_noinfo.txt
5.2.1 dumpsys命令介绍
dumpsys这个指令很有效,除了可“dumpsys meminfo+包名”以抓取内存,还可以“dumpsys package +包名”查看包信息(此前有看到其他同事利用这个指令查看应用是否有system flag来确定是否是由于没有system标识而被冻结导致的ANR),“dumpsys gfxinfo +包名”查看表现渲染信息进而查看卡顿情况。
adb shell dumpsys
■ meminfo 内存
■ cpuinfo CPU
■ gfxinfo 帧率
■ display 表现
■ power 电源
■ battery 电池
■ batterystats 电池状态
■ location 位置
■ alarm 闹钟
■ account accounts
■ activity 表现全部的activities的信息
■ window 表现键盘,窗口和它们的关系
■ wifi 表现wifi信息
5.3 排查效果
查看对比两份meminfo后结论为紧张是dex mmap带来的增长。
https://i-blog.csdnimg.cn/blog_migrate/109bfc9af519bd5999f2e5a757d1fe80.png
通过meminfo能查看内存的大概情况,进一步分析则需要借助抓取smaps文件
6.抓取smaps查看内存细节
我们通过dumpsys meminfo 获取内存时, 发现某一项内存数据非常,想弄清楚数据都是有哪些文件产生, 我们就可以通过读取smaps具体排查大概进行增量对比,smaps聚合统计到的数据, 可以清晰的看到哪一个dex、so、ttf、oat所占的内存,这部分信息adb shell dumpsys meminfo是不具有的。
6.1 smaps底子
利用smaps统计出来的内存和利用adb shell dumpsys meminfo是一致的,dumpsys meminfo 命令下的 Pss、Shared Dirty、Private Dirty这三列的数据是读取smaps文件天生。
基本信息含义如图:
https://i-blog.csdnimg.cn/blog_migrate/1941174be52ea0d9f6a3c1ab7e5ba128.png
6.2 抓取smaps
抓取smaps文件一定需要root权限才可以, 这也是手机厂商具有的优势,抓取命令如下:
adb shell cat /proc/$pid/smaps > smaps.txt,//需要root权限,无需一定要debug版本应用
由于测试开机内存需要清除数据,因此再介绍一个方便的清除数据的命令(高版本需要root)
adb shell pm clear com.xx.xx
smaps 记录了这个历程内存映射的原始信息,不外 smap 直接看的话并不是很友爱,一样平常是用一个脚本,让 smaps 和 meminfo 谁人效果结合起来看。
6.3 解析smaps
先介绍一下怎样利用smaps_parser.py脚本进行解析
解析脚本:
解析命令:
python D:\Users\80343288\Downloads\smaps_parser(1).py -f D:\Users\80343288\smaps.txt >smaps_parsed.txt
解析后的文件:
https://i-blog.csdnimg.cn/blog_migrate/a9d73b7975c69b4852debb6fa6af202b.png
6.4 排查对比smaps效果
https://i-blog.csdnimg.cn/blog_migrate/23982f4396ff3de960b1f5a80a5e8c3c.png
通过对两个版天职别抓取smaps解析对比,咱们知道dex mmap增长是由于base.vdex文件增长导致
dex mmap文件相差6MB左右,因此我再去对应目次查看了原始文件进行确认,发现两个原始vdex文件确实相差大概10MB左右,如图:
https://i-blog.csdnimg.cn/blog_migrate/d91de857a7c19c561fe8b6588aaf43a2.png
7.showmap
其实通过smaps已经定位到了题目,这里再额外介绍一下showmap
在smaps不能被解析之前是比较难分析的,因此也可以直接抓取showmap查看内存情况,showmap 就是解析了 smaps 的信息,这里可以看到历程中每个打开的文件所占用的内存,但是相比解析后的smaps,照旧不如smaps直观,没有分类汇总。
抓取命令如下:
adb shell showmap –t $pid > showmap.txt //需要root权限,无需一定要debug版本应用
二、分析题目
综上,我们通过一系列工具知道开机内存增长是由于base.vdex文件增长导致,那我们接下来首先弄清楚vdex是什么,以及他和dex、odex、oat、art文件的关系是啥?
1.dex相干概念
1.1 dex
dex 文件是可被Dalvik虚拟机辨认并执行的文件。
JVM执行的.class文件通过dx.bat工具就可以转换为dex ,Dalvik 会执行 .dex 文件中的 dalvik 字节码,但一样平常Dalvik在执行dex优化后的文件(即odex文件)。
1.2 vdex(Verified Dex)
vdex文件是 Android O (Android 8.0) 新增的格式包,其目的是为了低沉dex2oat时间。
为了避免不必要的验证Dex 文件合法性的过程,例如初次安装时进行dex2oat时会校验Dex 文件各个section的合法性,这时间利用的compiler filter 为了照顾安装速率等方面,并没有采用全量编译,当app盘启动后,运行一段时间后,收集了足够多的jit 热点方法信息,Android会在背景重新进行dex2oat, 将热点方法编译成呆板代码,这时间就不用再重复做验证Dex文件的过程了
1、当体系OTA后,对于安装在data分区下的app,由于它们的apk都没有任何变化,那么在初次开机时,对于这部分app假如有vdex文件存在的话,执行dexopt时就可以直接跳过verify流程,进入compile dex的流程,从而加速初次开机速率;
2、当app的jit profile信息变化时,background dexopt会在背景重新做dex2oat,由于有了vdex,这个时间也可以直接跳过
1.3 odex(Optimised Dex)
odex是OptimizedDEX的缩写,表示经过优化的dex文件,存放在/data/dalvik-cache目次下。
由于Android程序的apk文件为zip压缩包格式,Dalvik虚拟机每次加载它们时需要从apk中读取classes.dex文件,这样会泯灭许多cpu时间,而采用odex方式优化的dex文件,已经包含了加载dex必须的依赖库文件列表,Dalvik虚拟机只需检测并加载所需的依赖库即可执行相应的dex文件,这大大收缩了读取dex文件所需的时间。
对于dalvik虚拟机,odex存放的是JIT后的优化后的字节码(Optimized Dalvik EXcutable file)
对于ART,odex存放的是经过AOT(Ahead Of Time)编译后的当地呆板码(即:oat文件,一种私有的ELF文件格式)
在Android N之前,Dalvik虚拟机执行程序dex文件前,体系会对dex文件做优化,天生可执行文件odex,保存到data/dalvik-cache目次,末了把apk文件中的dex文件删除。
在Android O之后,odex是从vdex这个文件中 提取了部分模块天生的一个新的 可执行二进制码 文件 , odex从vdex中提取后,vdex的大小就淘汰了。
具体过程:
1.第一次开机就会天生在/system/app//oat/下
2.在体系运行过程中,虚拟机将其 从“/system/app”下 copy到 “/data/davilk-cache/”下
3.odex + vdex = apk的全部源码 (vdex并不是独立于odex的文件,odex + vdex才代表一个apk)
1.4 oat
深知大多数程序员,想要提升技能,往往是自己探索发展,但自己不成体系的自学效果低效又漫长,而且极易遇到天花板技术故步自封!
https://i-blog.csdnimg.cn/blog_migrate/1e210328c1ebf43f08d3b6c5f5c13b48.png
https://i-blog.csdnimg.cn/blog_migrate/1e6405caff0590e4159ba2cb9a639628.png
https://i-blog.csdnimg.cn/blog_migrate/0cd4290d956f23137e77aef41f7b9617.png
既有得当小白学习的零底子资料,也有得当3年以上经验的小同伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目次截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,而且后续会持续更新
需要这份体系化的资料的朋侪,可以戳这里获取
(vdex并不是独立于odex的文件,odex + vdex才代表一个apk)
1.4 oat
深知大多数程序员,想要提升技能,往往是自己探索发展,但自己不成体系的自学效果低效又漫长,而且极易遇到天花板技术故步自封!
https://i-blog.csdnimg.cn/blog_migrate/1e210328c1ebf43f08d3b6c5f5c13b48.png
[外链图片转存中…(img-VzgbSAo9-1715279204258)]
[外链图片转存中…(img-e3GigVSd-1715279204258)]
既有得当小白学习的零底子资料,也有得当3年以上经验的小同伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目次截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,而且后续会持续更新
需要这份体系化的资料的朋侪,可以戳这里获取
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]