用更少的内存来运行Android
简介
android现在可以运行在只有512M RAM的设备上了。本文就是用来帮助OEM厂商进行配置和优化,使得Android4.4运行在低内存设备上。
部分优化措施因为比较通用,也可以在前几个android版本上部署。
Android 4.4 平台优化策略
优化内存管理
检查内核节省内存的相关配置,如KSM(Kernel Same-page Merging)配置,ZRAM(压缩内存区)配置等。
结束掉那些缓存过大并且即将换出的进程。
禁止占用太多资源的服务在后台运行(当然,还是要保证luncher能正常运行的)
结束掉那些在空闲状态占用大量资源的进程(即使是一些平常被认为不可随便结束的进程,如输入法管理IME)
序列化后台加载的服务
调整那些小内存设备的内存使用,更加苛刻的oom调整机制,更小的图形缓存等。
缩减系统所需内存
对system_server和SystemUI processes进行裁剪(可省出来几MBs内存)
dalvik虚拟机预加载dex缓存(可省出来几MBs内存)
dalvik虚拟机关闭jit。(每个进程差不多省出来1.5MB )
缩减每个进程的字库缓存。
用更轻量级的 HashMap/HashSet 来替换之前framework中使用的ArrayMap/ArraySet。
关于进程状态
android4.4 添加了一个新的开发者选项,用来显示内存状态和应用的内存使用情况,按照使用频率和占用内存的量来排名。
API
新增加了一个api: ActivityManager.isLowRamDevice()
用来让应用开发者检测是否运行在低内存设备上,那时候就要关闭一些耗内存的属性了。
内存监视
新的内存监视HAL层实现了对图形内存使用情况跟踪,在meminfo的dump信息有了更多的内容,会有一个简单内存使用统计呈现,例如缓存进程的地址空间的空闲内存,有这个的话,就不至于做一些不合理的优化。
编译时候的配置
开启小内存标志位
上文中我们介绍过新API:ActivityManager.isLowRamDevice(),
对于 512MB 内存的设备,如果在makefile中做了如下系统属性配置:
PRODUCT_PROPERTY_OVERRIDES += ro.config.low_ram=true
那么,就意味着标记了小内存设备,该API此时就会返回TRUE。
关闭JIT
整个系统所需的JIT内存空间取决于正在运行的应用数量和这些应用的运行时的执行路径。JIT会初始化一个尽可能大的翻译缓冲区阈值以满足其需求。一般在系统运行期间,JIT消耗3M-6M左右的空间。
规模比较大的应用倾向于迅速地消耗完指令缓存区(一般为1M),平均地看,一个应用JIT的指令缓存使用在100K-200K之间。减小最大阈值有助于省出内存,不过,如果这个阈值设定得太小,也会导致JIT运行产生抖动。
如果在一个小内存设备上,我们建议完全关闭掉JIT功能。
可在MAKEFILE中添加如下配置实现:
PRODUCT_PROPERTY_OVERRIDES += dalvik.vm.jit.codecachesize=0
Launcher的配置
请确认默认墙纸不要使用动态墙纸。小内存设备不会预装动态墙纸。
KERNEL配置
1.
调整kernel/ActivityManager 内存配置参数,对 “直接回收”的边界值做合理设定。
当有进程想要申请内存(不管是直接申请还是缺页机制),可KERNEL已经用光了所有可用内存的时候,就会触发直接回收机制。
这就要求KERNEL在释放一些内存页的时候,阻塞申请请求。此时一般会释放缓存并同步到磁盘文件去,或者等lowmemorykiller来杀掉某个进程。这就可能对某些线程(包括UI线程)产生一些额外的I/O开销。
为了避免直接回收,KERNEL有一个触发kswapd或者后台回收的边界值。kswapd是一个守护进程,用来尽量释放一些页面,使得下次有用户进程申请的时候能迅速地获得。
默认的触发直接回收的边界值非常小,在一个内存2G的设备上差不多2M左右,512M的只有636KB。并且后台回收中KERNEL也只能回收几MB而已。这就意味着任何紧急申请多于1M的进程都会触发直接回收。
在android 4.3 kernel 上打上了一个调节kbyte释放的补丁,支持一种新的KERNEL调整。这个补丁(Cherry-picking)允许ActivityManager 通知KERNEL尽量保留3个全屏32位bpp的可用缓存空间。
这个边界值可以通过framework里面的config.xml来配置。
2.
调整LowMemoryKiller的触发边界值。
3.
采用KSM (Kernel samepage merging)机制。
4.
交换内存到zRAM块。
5. 对CMA(Contiguous Memory Allocation)区域采用Ion机制
ANR 三种情况
1 键盘输入
KeyDispatchTimeout(5 seconds) --主要类型按键或触摸事件在特定时间内无响应
staticfinal int KEY_DISPATCHING_TIMEOUT = 5*1000
2 接收者者处理超时
BroadcastTimeout(10 seconds) --BroadcastReceiver在特定时间内无法处理完成
3 服务
ServiceTimeout(20 seconds) --小概率类型 Service在特定的时间内无法处理完成
UI线程及Android的单线程模型原则
当应用启动,系统会创建一个主线程(main thread)。
这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件(components from the Android UI toolkit (components from the android.widget and android.view packages))发生交互。
所以main thread也叫UI thread也即UI线程。
系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去。
结果就是,响应系统回调的方法(比如响应用户动作的onKeyDown()和各种生命周期回调)永远都是在UI线程里运行。
原则:
1.不要阻塞UI线程。
2.不要在UI线程之外访问Android UI toolkit(主要是这两个包中的组件:android.widget and android.view)。