1. 基础知识
内存管理是Linux/Android OS的灵魂,关于内存的知识太多,此章节只为了后续的内存评估和优化对常用知识进行梳理,不对具体概念进行讲解,有必要的话需要先查阅相关知识点。
1.1 Linux内存地址空间
内存管理最基础的两个概念就是物理地址和虚拟地址,下图是最通用的描述虚拟地址通过MMU转换成物理地址的过程图。
接下来是32bit OS常用的虚拟地址分布图,03GB地址空间用户态进程占用,34GB地址空间分配给内核态使用。
1.2 进程地址空间
此处借用宋宝华博客中的一张图来说明进程地址空间中VSS/RSS/USS的关系。进程号1044的进程VSS=1+2+3,RSS=4/3+5/2+6,USS=6。在后面的分析中RSS是我们最关心的值。
1.3 Linux内存分配
用户空间的内存分配函数有
- mmap, 分为文件映射和匿名映射。
- malloc,通过brk系统调用实现的,对应的映射为匿名映射。
内核空间的内存分配方法有
- alloc_pages,基于伙伴系统实现。
- slab,面向对象的内存分配,代表的api是kmalloc。
1.4 Android虚拟机内存分配
ar堆内存由如下几个space组成
app运行时的虚拟内存空间分布,可以通过下图显示。
1.5 常用工具
- cat /proc/meminfo
推荐参考http://linuxperf.com/?p=142,文章对meninfo中所有item项都进行了解释说明 - procrank
按照内存空间占用的多少进行排序 - librank
从共享库角度分析 - dumpsys meminfo
从framework层角度查看系统内存的状况 - cat /proc/[pid]/[s]maps(pmap, procmem)
对具体进程进行分析
2. 内存优化
下面是一些内存优化过程中尝试使用的方法,会对他们的原理和作用进行说明,很多方法都来自于网络的学习,但是并不是网上提到的每种方法都有实际作用,这些在下面的作用说明中会提到。基于的平台是rk3368 Android 6.0。
2.1 32bit Android
原理:这个也较好理解,目前Android基于的硬件平台已经大多是64bit SOC,基本都会既支持32bit也支持64bit。64bit的app和lib相对32bit都会大些,也会占用更多的磁盘空间,运行时也会占用更多的内存空间。
一般工程lunch的时候都会提供32bit编译和64bit编译的选项,如果是对内存使用非常敏感的项目建议可以尝试使用32bit Android编译。
作用:影响选用的app和lib的大小,内存优化效果明显。
2.2 应用裁剪
原理:可以直观理解,使用的app少了,占用的内存空间和磁盘空间都会减少。
作用:影响app的数量,内存优化效果明显。
2.3 开机自启动优化
原理:开机后启动的应用数量减少,开机后马上查看,可用内存会有明显提升。rk平台有一个宏BOARD_WITH_MEM_OPTIMISE可以进行操作,也可以添加各种名单功能进行开机阶段启动应用的控制。
作用:减少开机阶段启动应用的数量,开机阶段有效果。
2.4 SWAP & ZRAM
原理:swap分区是在物理内存不足时,可以将不常用的内存交换出去。在内存回收时使用,回收的方式一个是将file相关的内存进行回收,一个是将anon部分内存(即被分配出去的内存)交换到swap分区。zram swap即是通过压缩内存来作为交换分区。
通过修改swappiness可修改swap分区的利用率,也可以修改zram相关参数,比如压缩算法从lz0改为lz4等来改善压缩效率。
作用:在物理内存不足时提高回收内存效率,可增加可用内存。
2.5 虚拟机参数调整
原理:dalvik.vm.heapstartsize, dalvik.vm.heapgrowthlimit, dalvik.vm.heapsize分别为虚拟机初始堆大小,极限堆大小,使用大堆时的极限堆大小。这些参数与应用的启动速度相关,也会影响应用所能使用的虚拟地址空间大小,但是却与物理内存无关。看修改前后两个procrank的结果就更清晰了。
作用:影响应用的启动速度,可限制应用的最大使用内存,与减少物理内存无关。
2.6 preload class
原理:preloadclass是可以去掉的,因为系统最终还是会在使用这些类时去加载,但这样就破坏了android采用fork机制来创建java进程的本意,而fork机制的好处是显而易见的:(1).zygote预加载的这些class,在fork子进程时,仅需做一个复制即可。这就节约了子进程的启动时间。(2).根据fork的copy-on-write机制可知,有些类如果不做改变,甚至都不用复制,它们会直接和父进程共享数据。
作用:对物理内存无影响。
2.7 LowMemoryKiller
原理:物理内存不足时,通过adj,minfree等参数对不同级别进程发起回收。LowMemoryKiller的值的设定,主要保存在2个文件之中,分别是/sys/module/lowmemorykiller/parameters/adj与/sys/module/lowmemorykiller/parameters/minfree。adj保存着当前系统杀进程的等级,minfree则是保存着对应的阈值。
作用:修改阈值对系统可运行的进程数量和可用物理内存有影响。
2.8 KSM
原理:KSM是一个运行在后台的内核线程,用来比较内存中哪些页在上层程序运行时被标记为MADV_MERAGEABLE;如果发现两个page相同,KSM线程就会把它们合并到一个写时拷贝的页中。
作用:提高内存回收效率。
2.9 根据pid中加载内容进行裁剪
原理:内存优化一般先用procrank对占用内存的进行排序,然后针对占用内存最大的几个进行进程具体分析,读取它们对应pid下的[s]maps信息,看下物理内存都被哪些占用了,如果是应用进程的话,可以看是否加载的库不需要可以进行裁剪。
但是所有应用都加载的language & ttf等信息裁剪对物理内存影响不大,因为一般情况下只会加载一种,也就是只有一种会分配物理内存,其他的只占用虚拟空间而已,但是裁剪这些能够减少磁盘空间的占用。
作用:这对具体进程进行分析,保证不会用到的库等可以进行裁剪。