1.1Android的官方文档主要提供了四种优化策略
1.Disable JIT,运行时动态编译,JIT编译出来的本地代码体积通常比较大,按官网的说法,运行事的系统大概多花费3-6m内存,一个大型app一般多花费1m,平均而言,一个app多占用100K and 200K bytes
2.lowmemorykiller,在达到某个内存门限的情况下去选择进程删除来释放内存,通过/sys/module/lowmemorykiller/parameters/minfree配置相关参数来杀掉次要进程,每个应用都会有一个进程的优先级。通过杀次要进程来释放内存。
3.KSM (Kernel samepage merging),内核同页合并,这特性允许更有效的处理内存,系统运行的越久越会节省内存,由于ksm是在后台运行的后台线程,在使用ksm会造成cpu性能损耗和消耗更多的电池,根据网上的说法是10%,目前没有相关的测试手段还没有测试。
4.Swap to zRAM,zRAM 因为需要开辟一小块内存作为 compressed block 使用,让系统当作虚拟内存来使用。传统的虚拟内存是存放在磁盘上的,而zRAM存在内存里,并会进行压缩,以达到“增加ram容量”的目的 。这样的虚拟内存访问速度可以提高很多,内存利用率也会大幅提高。目前我配的内存的压缩比是50%,文档介绍是在30%-50%,最优值需要大量的测试,在性能和内存之间找到一个平衡。
1.2系统gpu优化
优化GPU驱动,复用ION内存,解决游戏不能运行 问题,ION占用80M内存是不能避免的,但可以修改GPU驱动,让游戏可以使用ION内存,从而解决游戏因为内存不足闪退问题。
mali400驱动修改如下:
1,mali已分配页数超过mali最大可分配页数一定比例
2,当前进程的rss size超过一定比例
3,当前进程的adj=0
当以上三者同时满足的情况下,mali将优先从ion获取内存,如果ion没有足够内存,则转向os memory继续分配物理页,如果此时os memory无足够内存可供分配,则认为当前系统内存已饱和。
2在系统中实现
2.1Disable JIT:
在系统的makefile文件中加入下面一行
PRODUCT_PROPERTY_OVERRIDES += dalvik.vm.jit.codecachesize=0
设置为0之后会节省内存,但是会导致虚拟机的性能下降,在跑antutu发现设置0导致跑分变低,设置为200k基本没有影响。
2.2内核进程kswapd管理内存
Unix系统能够利用磁盘存放超过物理内存容量的部分内存内容,称作交换空间(swap space)。Kswapd就是用来处理页面的交换,它可以在内存不足时,将一些进程的页面交换到swap空间之中。在 kswapd 中,有2 个阀值, pages_hige和pages_low,当空闲内存页的数量低于 pages_low 的时候, kswapd进程就会扫描内存并且每次释放出32 个free pages,直到 free page 的数量到达pages_high.
kswapd和pages_low、pages_high这两个参数的设置值很有关系:
l 增加pages_low的值,可以使得kswapd不要等到内存消耗得太厉害再开始释放页面,这样似乎可以增加内存成功分配的几率。
l 增加pages_high的值,可以使得kswapd每次多释放一些内存,这样对于大型应用软件的使用,可以减少调用kswapd进行内存释放的次数。不过这样做带来的缺陷是,如果系统始终释放不出pages_high这样多的内存,那么是否会陷入一种死循环,或者过多地调用OOM函数?因此,如果pages_high设置得比较高,那么需要一种平衡机制。
pages_low、pages_high的值是在函数void setup_per_zone_wmarks(void)中进行设置的,这个函数位于kernel/mm/page_alloc.c之中。
Pagas_low的值可以在framework config.xml设置
<integer name="config_extraFreeKbytesAbsolute>-1</integer>直接赋值
<integer name="config_extraFreeKbytesAdjust">0</integer>在默认值(3个全屏大小)上加上这个值
2.3Low Memory Killer
Android中,进程的生命周期都是由系统控制的,即使用户关掉了程序,进程依然是存在于内存之中。这样设计的目的是为了下次能快速启动。当然,随着系统运行时间的增长,内存会越来越少。
Android 采用特殊的内存管理机制,Android Kernel会定时执行一次检查,杀死一些进程,释放掉内存。Low Memory Killer是在标准linux kernel的OOM基础上修改而来的一种内存管理机制,当系统内存不足时,杀死Bad进程释放其内存。
Low Memory Killer的源代码在drivers/staging/android/lowmemorykiller.c中,它是通过注册Cache Shrinker来实现的。Cache Shrinker是标准linux kernel回收内存页面的一种机制,它由内核线程kswapd监控,当空闲内存页面不足时,kswapd会调用注册的Shrinker回调函数,来回收内存页面。
(1)每个程序都会有一个oom_adj值,这个值越小,程序越重要,被杀的可能性越低。
(2)进程的内存,通过get_mm_rss获取,在相同的oom_adj下,内存大的,优先被杀。
(3)Android提供了两个数组,一个lowmem_adj,一个lowmem_minfree。前者存放着oom_adj的阀值,后者存放着minfree的警戒值,以page为单位(4K)。如下图:
以上就是两个阀值。
<integer name="config_lowMemoryKillerMinFreeKbytesAbsolute">-1</integer>
<integer name="config_lowMemoryKillerMinFreeKbytesAdjust">0</integer>
以下是该阀值计算公式:
mOomMinFree[i] += (long)((float)minfree_adj * mOomMinFree[i] / mOomMinFree[mOomAdj.length - 1]);
2.4开启KSM
开启KSM,要打开驱动KSM配置CONFIG_KSM,然后加入下面的几行在你的init..rc文件中:
write /sys/kernel/mm/ksm/pages_to_scan 100
write /sys/kernel/mm/ksm/sleep_millisecs 500
write /sys/kernel/mm/ksm/run 1
驱动配置Kernel Features --->[*] Enable KSM for page merging
查看是否有效在控制台输入ksminfo -a
2.5开启ZRAM
ZRAM是linux的一种内存优化技术,基本工作原理是:通过划定一片区域,将压缩过后的硬盘数据放入该区域,以实现高速读取。
ZRAM的实现方式有两种:
1、实现内存压缩,以达到“增加ram容量”的目的;
2、压缩区域数据,以实现“快速读取”的目的。
在系统的fstab.xxboard下配置:
/dev/block/zram0 none swap defaults zramsize=,swapprio=<swap partition priority>
Zram size的值是强制配置的,表示要多少未压缩的内存预留给zram的,大概是30%-50%,这个需要观察的
write /proc/sys/vm/page-cluster 0
如果想要一次写入一页到swap区,在init.rc加入该行,0表示1页,1表示2页,2表示4页。
在`mount_all /fstab.X`后面加入该行: swapon_all /fstab.xxboard
驱动配置
General setup --->
[*] Control Group support --->
[*] Memory Resource Controller for Control Groups
[*] Memory Resource Controller Swap Extension
[*] Memory Resource Controller Swap Extension enabled by defaultDevice Drivers --->
[*] Staging drivers --->[*] Memory allocator for compressed pages
<*> Compressed RAM block device support
可以用一下的命令查看你配置是否生效:cat /sys/block/zram0/disksize
3Android低内存接口
ActivityManager.isLowRamDevice(),用这个判断是否是低内存设备,在andriod自带的系统应用中,就找到settings的应用有使用到这个接口,来决定系统的锁屏界面是否可以加载widget,设置ro.config.low_ram=true后为低内存设备,该功能可以使用,就是说低内存没有锁屏界面加载widget的功能。该设置是给应用提供的接口,应用程序确定他们是否应该关闭特定的内存消耗多的方法,以便能很好地工作在低内存设备上。
4查看系统优化后的运行状态
通过控制台命令dumpsys meminfo:
Total RAM: 524288 kB
Free RAM: 192308 kB (33624 cached pss + 133752 cached + 24932 free)
Used RAM: 154814 kB (132538 used pss + 5808 buffers + 324 shmem + 16144 slab)
Lost RAM: 177166 kB
ZRAM: 4 kB physical used for 0 kB in swap (262140 kB total swap)
KSM: 224 kB saved from shared 32 kB
3588 kB unshared; 77056 kB volatile
Tuning:48(large256), oom 122880 kB, restore limit 40960 kB (low-ram)
红色表示ZRAM和KSM使用的情况,48是dalvik.vm.heapgrowthlimit=48m(每个应用最大可分配的内存),256是dalvik.vm.heapsize=256m(单个虚拟机可分配的最大内存256m)。
Oom122880设置/sys/module/lowmemorykiller/parameters/minfree最后一个值,(见2.3)表示剩余内存超过这个值将开始杀死进程回收内存。
5优化前后对比
5.1开机后剩余内存:
以下优化后的meminfo是在开机时根据dumpsys meminfo命令打印出来的,上面是优化后的,一般剩下160m左右,机器上看剩下只有120m左右,以下可以很明显的看到有用了zram交换4k,ksm合并相同的内存也224kb,说明是优化有效果的,优化会比没有优化省几m的内存空间。
Total RAM: 524288 kB
Free RAM: 192308 kB (33624 cached pss + 133752 cached + 24932 free)
Used RAM: 154814 kB (132538 used pss + 5808 buffers + 324 shmem + 16144 slab)
Lost RAM: 177166 kB
ZRAM: 4 kB physical used for 0 kB in swap (262140 kB total swap)
KSM: 224 kB saved from shared 32 kB
3588 kB unshared; 77056 kB volatile
Tuning: 64 (large 384), oom 122880 kB, restore limit 40960 kB (low-ram)
===========================================================================Total RAM: 524288 kB
Free RAM: 172751 kB (32023 cached pss + 112916 cached + 27812 free)
Used RAM: 163733 kB (141609 used pss + 7528 buffers + 336 shmem + 14260 slab)
Lost RAM: 187804 kB
Tuning: 64 (large 384), oom 122880 kB, restore limit 40960 kB (high-end-gfx)
开机后剩余内存对比,这些数值随着使用时间加长得到优化更多
5.2经常使用的游戏内存使用分析
以下是两台机器跑捕鱼拷机1个晚上后捕鱼占用的内存,这个应用会节省4m左右。对比发现private clean,private dirty差别比较大
Private clean,包括该进程私有的干净的内存。包括该进程独自使用的so和进程的二进制代码段。
Private dirty,表示该进程私有的不跟disk数据一致的内存段堆(heap),栈(stack),bss段。
说明优化后在该应用的私有的内存占用有所减低,从而减低内存的消耗
-------- -------- -------- -------- -------- -------- -------- -------- -------- ------------------------------
start end virtual shared shared private private
addr addr size RSS PSS clean dirty clean dirty object
-------- -------- -------- -------- -------- -------- -------- -------- -------- ------------------------------
854420 49648 31572 11108 9212 4276 25052 TOTAL
-------- -------- -------- -------- -------- -------- -------- -------- -------- ------------------------------
start end virtual shared shared private private
addr addr size RSS PSS clean dirty clean dirty object
-------- -------- -------- -------- -------- -------- -------- -------- -------- ------------------------------
849380 48084 28717 12504 9148 5420 21012 TOTAL