记载一次开机内存剖析的全进程,尽量详尽的介绍常用内存剖析东西和指令行的运用,结合详细问题讨论开机内存剖析的实践经历。经过这篇文章我会介绍开机内存的常用测验剖析东西的根本运用办法,以及怎么经过抓取出来的内存数据得出下一步的剖析思路,其中包括BootRemainMemory、MTN、Profiler、Mat、Meminfo、Smaps、Showmap。

文章概览:

一次收获满满的开机内存分析总结

问题布景:

测验搭档反馈开机内存有较大的新增,一同经过比照打包和去除事务X模块的版别发现问题可能出在事务X,因而咱们需求剖析详细是什么原因。

阐明:

剖析的这个问题仅仅一个引子,意图是想经过剖析这个问题,学习掌握内存剖析的常用东西和思路,所以内容会显得有些冗长,假如有不精确的地方欢迎讨论。

一、发现问题

1.运用BootRemainMemory测验开机内存

1.1 测验办法

依照测验办法进行多组测验。

运用BootRemainMemory需求留意几个点:

  • 手动掩盖装置运用和直接内置体系的版别的开机内存是有差异的,这儿据搭档说是因为两种办法对内存分区有不同的影响,因而咱们假如没有条件每次都刷内置版别只能掩盖装置,那侧重重视比照增量部分即可。

  • 假如设置间隔时刻过短也会导致测验数据动摇过大,因而这一步最好依照默许时刻测验,这也导致比较耗时。

1.2 测验成果

带事务X均值46.5

不带事务X均值43.4

成果:开机内存的确有3MB左右的添加

这儿我有个经历便是在跟测验对任何性能问题之前,有必要首要承认这几项内容防止浪费时刻:测验东西+测验办法+测验包+测验包的运转环境

2.排查是否引进带自发动功用的相关组件

我首要怀疑是否因为新增了自发动功用的组件导致内存新增,因而这儿先挑选排查内容供给器和静态播送。

2.1 ContentProvider

经过此前文章剖析ContentProvider的发动特性+此前的Provider优化经历,咱们知道Provider的发动机遇是跟从运用自发动,而且Provider的onCreate办法履行早于Application的onCreate,这就很简单带来开机内存的添加。

一次收获满满的开机内存分析总结

详见【从源码深化理解ContentProvider】/post/717249…

因而首要怀疑是事务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

一次收获满满的开机内存分析总结

2.1.3 排查定论

再结合Profiler查看的确有一个ProcessProvider在开机阶段被加载。

一次收获满满的开机内存分析总结

进一步需求排查该Provider的详细作用和对开机内存的影响规模,进一步咨询广告搭档,经过排查这个ContentProvider中生命周期onCreate中是否有反常内存运用状况。

承认定论为:现在初始化根本不触及事务内存占用。

2.2 BroadcastReceiver

静态播送在开机也会立即被注册,因而还需求排查是否有静态广告在开机后被初始化带动初始化了相关逻辑。

假如咱们在用户未赞同声明权限的状况 下,是不应该响应接收的功用的,这样也会间接引起内存的添加。

静态播送导致的开机内存添加的例子在咱们项目中此前也呈现过

因而咱们同样运用上面Provider的排查办法发现也没有对应的Receiver起来,排除这个原因的可能性。

3.运用Profiler定向排查怀疑的包名和类

当有确定的怀疑目标的时分,直接运用AS的Profiler进行查找会功率会更高,因而我查找了我能想到的事务X的类,只找到了DI注入相关的有必要类,没有相关事务加载。

一次收获满满的开机内存分析总结

3.1 Profiler根底

内存剖析器是 Android Profiler 中的其中一个组件,可帮助辨认可能会导致运用卡顿、冻住乃至溃散的内存走漏和内存抖动。它显现一个运用内存运用量的实时图表,能够捕获堆转储、强制履行垃圾收回以及盯梢内存分配。

这儿只介绍怎么运用Profiler查看内存堆转储文件的一些技巧,运用Profiler抓取内存文件或许获取hprof文件运用Profiler打开后便是以下界面。

一次收获满满的开机内存分析总结

4.运用Mat比照Heap改变

运用Profiler剖析仍是有必定局限,只能对已知怀疑的相关类进行逐一排查,对不知道的类引进我还不是很确定,因而我进一步抓取了两份hprof运用MAT进行比照剖析,企图找出详细的增量原因。

适合运用mat剖析的场景包括:需求比照某个行为前后的内存,需求比照两个不同版别的内存等

4.1 Mat根底和剖析技巧

4.1.1 Histogram

MAT中Histogram的首要作用是查看一个instance的数量,一般用来查看自己创建的类的实例的个数,经过查看Object的个数,结合代码就能够找出存在内存走漏的类,Histogram中还能够对目标进行Group,更便利查看自己Package中的目标信息

一次收获满满的开机内存分析总结

4.1.2 Dominator Tree

能够很简单的找出占用内存最多的几个目标,依据Percentage(百分比)来排序

Dominator Tree和Histogram的区别是站的视点不一样,Histogram是站在类的视点上去看,Dominator Tree是站的目标实例的视点上看,Dominator Tree能够更便利的看出其引证联系。

一次收获满满的开机内存分析总结

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,不包括虚、弱引证、软引证,剩余的便是强引证,一般用于查看内存走漏的根引证联系,还有其他几种模式就不一一解说了。

一次收获满满的开机内存分析总结

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// 查询悉数的反常目标以及其子类的实例

一次收获满满的开机内存分析总结

SELECT * FROM java.lang.String s WHERE s.count >= 100 //查询长度超越 100的字符串

一次收获满满的开机内存分析总结

SELECT * FROM INSTANCEOF java.util.HashMap s WHERE s.size>10//查询长度超越10的HashMap

一次收获满满的开机内存分析总结

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文件,因为每次混杂后的类名是不共同的,会导致比照可读性很低。

一次收获满满的开机内存分析总结

一次收获满满的开机内存分析总结

4.4 排查成果

从这儿的比照成果能够看到没有显着的目标分配数量反常,因为hprof只能观察Java堆内存的分配状况,因而添加应该来自于其他,需求抓取更完好的内存文件进行剖析。

留意:虽然咱们这个问题在这儿没有得到定论,可是一般状况下,要是有内存走漏或许是大内存的分配反常,经过这样比照两份内存文件再排序,就会很直观的暴露出来,这是很有用的办法。

5.抓取meminfo查看全体内存状况

5.1 meminfo根底和剖析技巧

运用meminfo能够查看指定进程或包名的内存全体运用状况,一般查看这个文件后能够确定是哪个内存大块出问题,而且这个内存信息相对来说获取本钱较低,是咱们剖析内存最常用的办法。

meminfo的数据来源是解析smaps等信息分类汇总而成,数据单位KB,详细原理见后面的参考文档。

5.1.1 meminfo根底解读

一次收获满满的开机内存分析总结

5.1.2 常用剖析思路

剖析思路一般是抓取两份meminfo进行比照查看,依据比照成果,依照以下路径进一步剖析是哪一部分的问题

  1. 假如是 Dalvik 部分内存变大,需求去查看 hprof 文件

  2. 假如是 Native 部分内存变大,需求去依据 Native Debug 的文档,合作 hprof 文件进行剖析,大部分 App 的 Native 内存变大都是 Java 层的调用导致的

  3. 假如是Graphics增大,则查看详细是 GL mtrack / EGL mtrack

    1. 需求查看 gfxinfo 的成果

    2. 需求比照两台机器的分辨率、App 的 SurfaceView 、TextureView、Webview 等运用状况

    3. 需求查看 App 硬件加速的运用状况

  4. 假如是 so / jar / apk / ttf 变大,进一步获取smaps查看 so / jar / apk / ttf 的个数,比照查看是哪部分变大,或许是因为多了哪个 so / jar / apk / ttf 导致的

  5. 假如是 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 [options]
■ meminfo 内存
■ cpuinfo CPU
■ gfxinfo 帧率display 显现
■ power 电源
■ battery 电池
■ batterystats 电池状况
■ location 方位
■ alarm 闹钟
■ account accounts
■ activity 显现悉数的activities的信息
■ window 显现键盘,窗口和它们的联系
■ wifi 显现wifi信息

5.3 排查成果

查看比照两份meminfo后定论为首要是dex mmap带来的添加。

一次收获满满的开机内存分析总结

经过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文件生成。

根本信息意义如图:

一次收获满满的开机内存分析总结

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

解析后的文件:

一次收获满满的开机内存分析总结

6.4 排查比照smaps成果

一次收获满满的开机内存分析总结

经过对两个版别别离抓取smaps解析比照,咱们知道dex mmap添加是因为base.vdex文件添加导致

dex mmap文件相差6MB左右,因而我再去对应目录查看了原始文件进行承认,发现两个原始vdex文件的确相差大约10MB左右,如图:

一次收获满满的开机内存分析总结

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

ART虚拟机运转的是oat文件,oat文件是一种Android私有ELF文件格局,oat文件包括有从dex文件翻译而来的本地机器指令,还包括有本来的dex文件内容(如下图所示),因而oat文件比odex文件更大。APK在装置的进程中,会经过dex2oat东西生成一个OAT文件(文件后缀仍是odex)。

关于apk来说,oat文件实际上便是对odex文件的包装,即oat=odex,而关于一些framework中的一些jar包,会生成相应的oat尾缀的文件,如system@framework@boot-telephony-common.oat

留意: Android5.0 及之后的版别,oat文件的后缀仍是odex,可是已经不是android5.0 之前的文件格局,而是ELF格局封装的本地机器码。能够以为oat在dex上加了一层壳,能够从oat里提取出dex

1.5 art

意图是用于加速运用发动速度。

art文件是由虚拟机履行odex文件后,记载虚拟机履行Apk发动的常用函数地址信息后生成出来的文件(记载函数地址信息便利寻址),一般会在data/dalvik-cache/ 目录中保存常用的jar包的相关地址记载

2.dex履行流程

一次收获满满的开机内存分析总结

3.dex mmap

dex mmap在Android运用中的作用是映射classes.dex文件。dalvik虚拟机需求从dex文件中加载类信息,字符串常量等;

还需求在调用函数的时分直接从mmap内存中读取函数代码(dvm bytecode)来履行,所以该部分内存是程序运转必不可少的。

一次收获满满的开机内存分析总结

这儿发生一个疑问,咱们经过前面的剖析能够确定hprof文件中并没有实例化导致内存添加的相关类,莫非dex mmap是一股脑悉数加载出来,并不是按需加载?

3.1 dex并非彻底按需加载

以一个示例运用为例,咱们能够在MAT中看到,运用加载了大约1500个class类型,而dex文件的class类型共有10635个。

运用dex mmap动态计算功用计算后发现,虽然只加载了1500个类,但dex内存一般却高达4-6M,差不多是dex文件巨细的一半。以上数据中能够看到,很大一部分dex内存空间被浪费了,实际运用到的数据和代码并没有那么多,这是为什么呢?

这是因为dex文件在生成时是按字母次序摆放。因为4K页面加载的原因,实际运转时会加载许多相邻但不会被用到的数据。例如在代码中运用了A1类,虚拟机就需求加载包括A1类数据的页面。但因为A1的数据只有1K,那在加载的4K页面中,还会有A2A3A4类,一共占用了4K内存。假定咱们的代码里在用到A1类后,还会用到B1C1D1类,那则会加载许多额外的dex文件。

一次收获满满的开机内存分析总结

3.2 dex优化

那么假如能在dex文件中将A1B1C1D1类放在一同,虚拟机就只需求加载一个4K页面,不仅削减了内存运用,还对程序的发动速度有好处。

因而,优化dex的思路便是调整Dex文件中数据的次序,将能够用到的数据严密摆放在一同,或许是对dex直接做减法,详细的思路有多种咱们放到后面汇总傍边介绍。

别的咱们也得到一个经历:在优化内存时,不只有堆内存,还有其他许多类型的内存能够进行剖析和优化,比方这儿的dex带来的内存。

三、开机内存优化思路汇总

经过前文的剖析,咱们文章最初说到的开机内存添加问题的原因很明晰:首要是由事务X静态代码和依靠库增多导致

针对这个dex添加的问题最直接的解法肯定是对代码做减法,这儿其实又和apk体积优化思路是相通的,但在日常开发中,随着需求的添加,代码量添加是必然的,有时分可能无法进一步减缩代码,咱们其实还有许多其他曲线救国的思路能够测验,这儿我做一个扩展并对搜集到的办法进行分类汇总。

1.Dex布局优化

将发动期间要履行的悉数代码添加到首要 classes.dex 文件中,一同将悉数非发动代码从首要 classes.dex 文件中移除,运用者供给程序发动时加载类序列作为配置文件,按此次序调整dex中类的次序,能够有用提升冷发动速度和优化dex mmap内存。

一次收获满满的开机内存分析总结

Dex布局重排有两种办法,能够运用官方的插件进行配置或许运用facebook的redex插件。

可是一个受限于环境,一个受限于gradle插件版别要求,完成有必定难度,但假如能落地成功这个作用估量比较显着。

【DEX 布局优化和发动配置文件】developer.android.google.cn/topic/perfo…

【Redex安卓Apk优化技术研究】blog.csdn.net/m0_62089210…

2.按需推迟加载

开机内存优化意图便是在在用户未赞同声明权限或许是在事务未正式运用之前,占用较少的内存。所以咱们的懒加载条件便是,仅有当用户赞同权限,才加载需求的内容;或许是需求真正用到对应的功用才初始化对应的事务类,防止悉数非必要的提前初始化。

2.1 Provider优化

–同一个运用IPC功用的Provider能够兼并多个provider为一个

–防止在Provider的生命周期函数中做事情

–借助Provider手动搜集事务逻辑拖延初始化

–仅仅作为初始化入口的运用StartUp代替

–运用手动调用替代借助Provider触发初始化(例如WorkManager、StartUp、Lifecycle、LeakCanary)

2.2 查看startup是否按需加载

startup结构默许也是开机就起来的,需求将默许的Provider移除然后运用手动推迟初始化的用法进行替代

 <provider
	android:name="androidx.startup.InitializationProvider"
	android:authorities="${applicationId}.androidx-startup"
	tools:node="remove" />

初始化WorkManager一共有三种办法:1是经过声明默许的WorkManagerInitializer初始化,缺点是没用到也会被加载 2是经过显式调用WorkManager.initialize手动初始化,缺点是没初始化就调用WorkManager.getInstance就运用就会溃散 3是Application完成Configuration.Provider接口按需推迟初始化

特别留意是防止跟startup绑定的WorkManager开机起来就触发读取WorkDatabase恢复履行守时使命,导致开机内存添加。

能够看到咱们的工程现在就运用了默许的发动办法,未对InitializationProvider配置remove,具有开机内存危险,能够优化。

一次收获满满的开机内存分析总结

2.3 防止非必要三方库提前加载

比方RxJava推迟初始化,能够运用自定义线程池替代线程分发

2.4 静态播送慎用

静态播送开机起来就会开接收外部播送,很有可能会导致一些不必要的内存占用,能够移除该静态播送考虑用动态播送代替

2.5 推迟初始化任何非有必要类和特点

在用户进入赞同权限后或许在进入对应页面真正要用到该类的时分再触发对应事务类的初始化

对特点运用by Lazy

2.6 Koin优化

2.6.1 按需注册

添加按需懒注册,用于在权限声明赞同后再注册。

比方现在咱们项目中Koin进程起来就注册了悉数的类,是有优化空间的,能够新增一个注解类型用于操控注册机遇,防止开机就注册,现在占用内存如下

2.6.2 懒注入

运用inject办法获取注入目标,防止运用Koin.get获取

//非懒加载,get()这儿的功用是直接检索实例(非推迟)
 val str : String = getKoin().get()
//懒加载,在用到的时分才注入
 val str2:String by inject()
 完成原理上injec仅仅对get进行了Lazy封装而已,机遇也是调用get办法

3.代码减缩

3.0 编码标准优化

对共用才能运用表格统一管理,便于查询防止不同的开发新增相同才能的重复代码

3.1 PB降级为JSON

因为PB的特性,导致很简单的一段代码也会生成巨量格局化代码,而且这巨量的代码还有必要不能混杂。

一方面PB是为了削减传输量进而下降网络负载而存在,但其实有些是非常低频的没必要一向占着客户端稀缺的内存,这儿也有一些优化空间。

另一方面则是查看是否有能够做减法的PB目标,比方服务端定义的通用接口有10个字段,可是咱们只需求用5个,则能够只定义这5个。

比方这个一个字段也用的pb,导致很简单的一段代码也会生成巨量格局化代码

一次收获满满的开机内存分析总结

3.2 经过扫描东西sonar查看重复代码进行优化

运用东西对重复代码进行查看并人工优化

一次收获满满的开机内存分析总结

4.代码混杂

4.1 添加混杂

混杂经过缩短运用的类、办法和字段的称号来减缩运用的巨细

混杂能够检测并删去未运用的类,字段,办法和特点

4.2 依据包名进行混杂分组

Dex文件中数据根本是按类名的字母次序进行摆放的,这样同样包名的类会排在一同。但在实际程序履行中,同一个package下的类并不会悉数一同调用,而是和许多其他package下的类进行交互,但mmap加载了整个页面,可能会有许多无用数据。为了削减这样的状况,咱们在生成文件时要尽量将运用到的数据内容排布在一同。在APK的编译流程中,Proguard混杂东西正好是能够对类名进行修改的,能够依据程序运转的逻辑,将那些会相互调用的类改为同一个package名,这样就能够使它们的数据排布在一同,经过混杂整理dex文件次序,削减因为4k约束导致打开的文件页巨细

4.3 削减混杂粒度

例如只豁免需求被xml文件引证的自定义View,而不是豁免悉数的自定义View

例如只豁免Parceable完成类中的CREATOR成员变量,而不是整个类

5.类库裁剪

5.1 集成运用lite库等相同功用但内存占用更小的库

5.2 只保留重复功用的其中一个库

例如视频播放器优化为只选用一个,图片库只集成一个