最近身边朋友参与车载项目或者手机项目的,他们都面对一个问题就是为了节约成本,降低内存.我目前碰到两个案例,一个车载项目Android8.0 计划从2GB降低到512MB, 另一个是Android6.0的手机项目.
所以针对这类需求我们应该如何下手呢?内存裁剪的目标是什么呢,占用越少越好?
目标
我们先回答第二个问题,我们裁剪内存的目标是多少呢?根据Android go的建议,针对512MB的设备有一个推荐,大家可以参考一下下面的截图。
通过上面我们大致清楚我们的优化目标了,考虑到google会集成进去gms app,这块会占用50-100MB内存.那么多针对HVGA和WVGA的设备,在抛开GMS的情况,设备差不多目标能够在200MB左右即可满足. 当然设备不同,需求不同,内存也不一样,比如车载设备没有NFC,即可节约30MB常驻内存,没有modem可以节约80MB等等,所以后面具体问题具体分析。
怎么做呢?
第一: 确认当前内存的使用情况
1.读取物理硬件的内存占用
adb shell cat /proc/meminfo读取出MemTotal的大小,用总的内存大小减去这个Memtoal就得出物理设备占用的大小,如果想知道这块具体分配情况,有两个办法:
A.通过读取串口log,每次串口开机时候都会打印出来内存分布情况
B.在lk或者高通的amss代码里面有相关源码可以参考,具体咨询底层或者芯片厂商
2.读取开机到launcher的内存占用情况
Android提供一个命令dumpsys,可以执行adb shell dumpsys meminfo,比如下面的结果:
过上面图标可以看出整体的内存分配情况,基本上可以分为几类:A.常驻内存进程,不开被回收(Native) B.需要常驻内存,但是可以被回收,杀掉的话影响UE体验(Forground,Visibile,Perceptible,A services,Previous) B.可回收内存进程 (B service,Cached app)
第二: 针对内存使用情况进行当前设备的需求分析
我们针对上文内存占用情况进行分析,我们内存裁剪主要从三个方面着手:
1.分析硬件模块需求,调整物理模块的内存占用
就个人经验来看,底层很多内存都不怎么使用,而预留了,比如说NFC,很多项目不支持,而硬件也预留了,比如bootlogo这块,有些项目不需要这个
2. 针对常驻内存做内存优化和裁剪
比如某些常驻内存的service不怎么经常使用,却一直占据后台,改变内存分配方式节约内存(MALLOC_SEVLTE)等等
3.早点释放后台进程内存,增加内存扩展
调整LMK的参数,早点杀掉退到后台的进程。限制cache进程的数目。
开ZRAM,增加内存
第三: 已知的可以优化或者裁剪方案
1.设置设备为low ram
改善了内存管理
减少了系统内存占用
具体参考:https://source.android.com/devices/tech/perf/low-ram
2.64位改成32位
据内部测试来看,内存可以节约20%左右
3.改变内存分配方式,je malloc
MALLOC_SVELTE := true
4. 打开ZRAM
5.调整dalvik参数
6.调整lmk参数
目前go用的lmkd管理,不再按照之前按照内存剩余级别杀掉进程。
7.其它native模块的裁剪或者优化
比如camera优化,具体和项目的camera负责人员沟通
8.system_server裁剪和优化
systemserver进程中会启动很多service,里面还有几个运行宏,可以动态关闭,不过需要修改代码,我关闭了大部分,开不了机器,修改了很多代码才能开机,不过还是能节约部分内存的。
8.app优化
很多app不仅仅提供ui功能,还要provider功能,这样导致部分app在退出到后台后进程优先级还是比较高,很难被lmk回收掉,这种情况我们可以把provider和ui部分隔离开来,当然系统中很多这样的案例,Settings和SettingsProvider
还有些app开机就启动,并且绑定核心service或者指定persistent属性不能被杀掉,比如nfc进程,这种情况可以分析代码逻辑,开机不让其启动。
9.其它优化
每个项目的需求不一样,大家根据自己的项目需求可以裁剪一下其它的,比如有些对launcher不看中,那就换一个简单一点的launcher,有的项目部需要systemui,那就拿掉systemui等等
总结
以上介绍了内存优化的流程,很多都是以内存为目标,这个还是有点太狭隘,最终目标的用户体验,如果用户体验不好,那么你优化的措施就不能实施,所以大家在做内存优化的时候要多关注一下性能问题
三、Kernel层
针对系统剩余内存方向出发
1 针对文件页和匿名页回收,增加swapiness. 尽量使用匿名页回收。
2 zram的使用。尽量将不常用的应用放入内存。!!!
3 proc/meminfo中的Mlocked的页尽量去掉。 建议关闭。
4 配置MALLOC_SVELTE :=true 来disable tcache. 目前系统默认使用的是jemalloc
[DESCRIPTION]
KK及以前版本默认使用dlmalloc,没有其他选择,从L开始,引入了jemalloc,并且作为默认的内存分配器。jemalloc在多线程环境下性能好于dlmalloc,不过调试比较困难。有时为了调试需要将jemalloc切换为dlmalloc。以下提供切换方法。
[SOLUTION]
有2个地方可以切换,效果一样:第1个地方:/device/$company/$project/BoardConfig.mk添加:
MALLOC_IMPL:= dlmalloc
第2个地方:/device/mediatek/$project/ProjectConfig.mk添加:
MALLOC_IMPL= dlmalloc
其中$company为对应公司名,$project为项目名。
MALLOC_IMPL值可以为dlmalloc或jemalloc,如果没有定义MALLOC_IMPL则默认为jemalloc。
5
底层内存裁剪的一些思路:
主要思路是针对功能需求,裁剪冗余或无用的功能项,可以从以下几个方面下手:
1、kernel config的逐个排查,去掉冗余的项
结合功能需求去掉无用的功能模块,非必要的调试选项,比如安全/加密部分,USB的多余外设支持,FS的多余支持
2、缩减reserved的内存占用
从dts中声明 reserved 或代码中申请reserved的部分下手
3、缩减未进入内存管理的内存占用
找出物理内存 - MemTotal(/proc/meminfo中第一行)被哪些地方使用, 看能否裁剪一些
4、从占用较大的内存块下手,找到谁在使用, 看能否裁剪一些
/proc/vmallocinfo 中较大的部分
/proc/slabinfo 中较大的部分
5.从多余的native进程下手
从adb shell ps -A中排查非必要的进程和服务,进行裁剪
如何查看kernel占用的内存:
1、未进入内存管理的内存
即是物理内存 - MemTotal(/proc/meminfo中第一行)的部分。
2、kernel reserved内存
kernel reserved的内存,即是kernel log中Memory:的reserved部分的大小
举例如下:
//代码占用 = kernel code + rwdata + rodata + init + bss//reserved = reserved + cma-reserved//关系:reserved_pages(63776K) = physpages(2045952K) - totalram_pages(1982176K) - totalcma_pages(0K)[0.000000]-(0)[0:swapper]Memory:1982176K/2045952K available(12924K kernel code,1384K rwdata,4392K rodata,960K init,5936K bss,63776K reserved,0K cma-reserved)
3、kernel运行中分配的内存
对应dumpsys meminfo 中Used RAM:中kernel部分的大小
这里kernel的占用是从/proc/meminfo和/proc/vmallocinfo中统计而来,具体上:
kernel used = Shmem + SUnreclaim + VmallocUsed + PageTables + KernelStack
Shmem,SUnreclaim,PageTables,KernelStack对应/proc/meminfo中的具体字段
VmallocUsed 是统计/proc/vmallocinfo中除ioremap,map_lowmem,vm_map_ram之外的和
Total RAM:1,983,136K(status critical)Free RAM:1,116,972K(0K cached pss+203,672K cached kernel+913,300K free)Used RAM:873,491K(629,723K used pss+243,768K kernel)Lost RAM:-7,331K ZRAM:4K physical usedfor0K in swap(1,048,572K total swap)Tuning:128(large256),oom322,560K,restore limit107,520K(high-end-gfx)