Android低内存配置

Android低内存配置

      • 概要
      • 平台优化
          • 改善内存管理
          • 裁剪系统占用内存
      • 内存使用统计
      • 编译配置
          • 打开低内存配置
          • 启动器配置
      • 内核(kernel)配置
          • 减少kernel或者应用直接触发前台内存回收再利用
          • 打开低内存杀进程功能
          • 使用Swap to zRAM功能
      • 使用CMA
      • 应用优化建议
      • 理解多种进程状态
      • 分析
          • 分析应用启动时间
          • 使用dumpsys分析
          • 分析常驻进程
          • 压力测试

官方原文:
https://source.android.com/devices/tech/perf/low-ram?hl=en
该文档以Android4.4 为例,下面内容不再做说明,翻译出此文档,旨在参考,以优化使用Android系统。

概要

android支持设备使用512M低内存。有以下优化点在系统中使用

平台优化

改善内存管理
  • 节省内存配置:Swap to ZRAM
  • 杀掉占用很大内存的缓存进程
  • 不允许一个大的后台服务常驻在A类型service中
  • 杀掉占用太多内存且未被使用的进程,比如输入法,确保系统维护足够的内存
  • 对后台服务的启动进行序列化
  • 优化了低内存设备的内存使用方式:采用更严格的内存不足 (OOM) 调整级别、缩减图形缓存大小,等等
裁剪系统占用内存
  • 裁剪system_server和systemUI(节省几兆内存)
  • 预加载dex到Dalvik(节几兆内存)
  • 关闭JIT(每个进程节省1.5M)
  • 减少各进程的字体缓存
  • 使用ArrayMap/ArraySet代替HashMap/HashSet

内存使用统计

在开发应用中增加一些开发选项,显示应用内存状态和统计应用内存使用频率和内存消耗程度。

API
应用中使用ActivityManager.isLowRamDevice(),确保在运行时检测到地内存和关闭占用大量内存的特性。

内存使用跟踪
使用dumpsys meminfo,分类归总内存信息。其中free memory包含缓存进程占用的内存。

编译配置

打开低内存配置

配置

PRODUCT_PROPERTY_OVERRIDES += ro.config.low_ram=true

确保ActivityManager.isLowRamDevice()返回为true

启动器配置

请务必确保启动器的默认壁纸设置未使用动态壁纸。低内存设备不应预装任何动态壁纸。

内核(kernel)配置

减少kernel或者应用直接触发前台内存回收再利用

当kernel使用了所有可用内存,一个进程或者kernel申请一个内存页时,会触发内存回收再利用。这时,内存申请被阻塞,直到释放足够内存。内存回收再利用会把缓存的内存信息写在存储器(如emmc)中或者触发lowmemorykiller杀掉一些进程。这样做的结果是阻塞I/O和UI线程。前台

为了避免直接出发内存回收再利用,内核可配置为触发kswapd或者后台内存回收再利用,确保下次申请内存时有足够的内存分配。

默认的后台内存回收再利用阈值很低,在2G的设备上阈值时2M,512内存设备上阈值是636KB,内存也仅仅可以回收很少的内存。意味着任何进程申请内存的速度快于内存回收的速度,都会触发前台内存回收再利用。

为了支持可配置,在android-3.4分支,patch 92189d47f66c67e5fd92eafaa287e153197a454f (增加额外内存回收配置)。合入该patch,默认ActivityManager 可以告知内核试着拥有3 full-screen 32 bpp buffers的内存。

// Ask the kernel to try to keep enough memory free to allocate 3 full
// screen 32bpp buffers without entering direct reclaim. 
int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
int reserve_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);
int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
...
SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));

该阈值在framework的config.xml中配置


-1

0

生效方式

$ grep -nr "sys.sysctl.extra_free_kbytes"
frameworks/base/services/core/java/com/android/server/am/ProcessList.java:297:            SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
system/core/rootdir/init.rc:664:on property:sys.sysctl.extra_free_kbytes=*
system/core/rootdir/init.rc:665:    write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
打开低内存杀进程功能

配置LowMemoryKiller 的阈值根据内存使用情况可变,当内存多次处于不足时,会增加内核中预准备内存值。当内存需求量不大时,会降低该阀值。


-1

0
使用Swap to zRAM功能

zRAM通过压缩内存页,然后放置在动态申请的交换内存区,增加内存的可用量。

由于zRAM会导致内存中交换,增加cpu占用,谨慎评估使用zRAM对系统性能的影响。系统处理swap to zRAM 有以下几个级别:

  1. 使用Swap to zRAM,内核必须打开以下功能
  • CONFIG_SWAP
  • CONFIG_CGROUP_MEM_RES_CTLR
  • CONFIG_CGROUP_MEM_RES_CTLR_SWAP
  • CONFIG_ZRAM
  1. 在分区表增加以下内容
/dev/block/zram0 none swap defaults zramsize=,swapprio=
  • zramsize 设置多少字节的未压缩内存被zram区域管理。压缩比例一般是30-50%
  • swapprio 如果没有多于一个交换区,可以不用设置

为交换区设置selinux权限

/dev/block/zram0 u:object_r:swap_block_device:s0
  1. 默认交换区有8个内存页。使用ZRAM,在内存压力大时,同一时间读取一个内存页时增加的性能消耗时微不足道的。同一时间读取一个内存页,init.rc中设置如下
write /proc/sys/vm/page-cluster 0
  1. 在init.rc中,在mount_all /fstab.X之后加上
swapon_all /fstab.X
  1. cgroups 在kernel中配置打开,开机时会自动生效
  2. 如果cgroups 可用,ActivityManager 标记一个低优先级线程作为内存交换。如果的确需要内存,kernel迁移内存页到zRAM swap,并把高优先级给到ActivityManager标记的内存页。

使用CMA

在低内存设备,碎片内存需要关注,尤其哪些不能被充分利用。比如一个视频回放内存分配。有多种解决方案把硬件需求的区域影响降低到最低(不太明白什么影响)
如果hardware准许使用非连续内存申请,ion system heap 可以申请内存在系统内存,排除内存分割。如果需要分配一段连续或者被配置特定地址的区域,使用CMA申请内存。CMA通过ion,直接简单的使用ion cma heap。

应用优化建议

  • 阅读 Managing your App’s Memory
http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html
http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html
http://android-developers.blogspot.com/2009/02/track-memory-allocations.html
http://tools.android.com/recent/lintperformancechecks
  • 删除asset下无用的文件(development/tools/findunused可以帮助)
  • 使用PNG,特别是使用透明区域时
  • native代码,使用calloc()而不是malloc/memset
  • 不要使用序列化数据,写到磁盘或读出来
  • 不要订阅安装的每个软件包,而是使用ssp过滤。添加如下所示的过滤


理解多种进程状态

  • SERVICE - service_restarting
    应用程序由于自己的原因使自己在后台运行。最常见的问题应用程序在后台运行时太多了。%duration * pss可能是一个很好的“坏”度量标准,虽然这个集合非常集中,只是做持续时间百分比可能更好地关注我们根本不想让它们运行的​​事实。
  • IMPORTANT_FOREGROUND - RECEIVER
    应用程序由于任何原因在后台运行(不直接与用户交互)。这些都为系统增加了内存负载。在这种情况下,(%duration * pss)badness值可能是这些进程的最佳排序,因为其中许多将始终运行的原因很充分,因此它们的pss大小作为其内存负载的一部分非常重要。
  • PERSISTENT
    持久存在的进程。跟踪pss以观察这些进程的内存变大。
  • TOP
    用户当前正在与之交互的流程。同样,pss是这里的重要指标,显示应用程序在使用时创建的内存负载量。
  • HOME - CACHED_EMPTY
    那些被系统缓存起来,可能还会用的进程,可以被随时释放或者重新启动。这是我们估算进程状态的基础,内存状态有:正常、节制的、地内存、严峻的,不同状态决定了可以缓存进程的数量。根据pss,这些进程获取的内存尽可能的小,以使更多的进程可以被缓存。通常,我们会有一个有意义的变化,处于改状态比TOP状态有更小的PSS。
  • TOP vs. CACHED_ACTIVITY-CACHED_ACTIVITY_CLIENT
    在TOP、CACHED_EMPTY,观察处于后台的进程如何释放内存,确保在后台使用比较少的内存。

分析

分析应用启动时间

使用adb shell am start,加上-P或者–start-profiler选项,启动app的同时启动分析器。该命令在虚拟机fork app 进程后立即启动分析器,在任何代码加载之前。

使用dumpsys分析

如batterystats, netstats, procstats, and usagestats等

分析常驻进程

重启系统,记录进程状态
运行几个小时后,再次记录进程状态,对比数据分析,此时不应该有长时间运行的进程

压力测试

长时间运行设备测试,监控内存变化。创建不同场景,压力测试分析变化

你可能感兴趣的:(Android系统优化)