背景
安卓内存优化,涉及应用层、框架层、内核层。应用层侧重于使用层面,须有良好的内存使用习惯。框架层、内核层则是在aosp的基础,进一步优化系统内存管理机制。
应用层内存优化,是为了app运行更加流畅,减少crash。框架层、内核层的内存优化,主要是为了提高可用内存、app保活数量、以及系统本身的流畅度。
内存基础知识
-
应用层
避免内存泄漏、OOM、延迟使用对象等,详情略。 -
框架层
- 进程ADJ
系统运行,内存过低时,根据进程优先级回收内存。ADJ代表进程优先级。
Linux 内核 通过 proc文件系统,暴露 /proc/[pid]/oom_score_adj文件,来允许其他程序修改指定进程的优先级。值越小进程越重要。内存紧张时,系统会遍历所有进程,以确定杀死哪个进程,回收内存,此时会读取 oom_score_adj 这个文件的值。
ProcessList.java中预定义了 oom_score_adj 的可能取值。
进程ADJ详解 - 尽情期待
- 进程ADJ
-
内核层
-
内存页分类
- 缓存页/文件页
存储器有文件支持- 私有页:干净页、脏页
- 共享页:干净页、脏页
- 匿名页
没有文件支持,临时产生
- 缓存页/文件页
-
page fault
缺页中断:访问虚拟内存内时,虚拟地址到物理地址未映射,或者有映射访问的page不在物理内存中,由cpu的MMU发出的中断。
遇到缺页中断,内核会从硬盘加载文件到物理内存。
缺页中断分类:
1、Hard Page Fault 物理内存中没有对应的页帧
2、Soft Page Fault 缺少VA到PA的映射。一般出现在多进程共享内存区域
3、Invalid Page Fault· 无效缺页错误,比如访问的内存地址越界 -
cpu访问设备简述
cpu通过 地址总线 访问连接在地址总线上的所有外设,包括物理内存、IO设备等。但从cpu发出的访问地址,并非这些外设在地址总线上的物理地址,而是虚拟地址。由MMU将虚拟地址转换成物理地址,再从地址总线上发出。 -
page table
每个进程都有自己的页表,页表存储了进程中虚拟地址到物理地址的映射关系。MMU收到cpu的虚拟地址后查询页表。确定是否存在映射以及读写权限是否正常。 -
memcg
即memory cgroup。管理系统中一组进程的内存行为,对内存有不同需求的进程区分管理,实现更有效的资源利用和隔离。
比如控制app的内存使用量。 -
block io
block I/O指的是块输入/输出,它是Linux内核中与块设备交互的输入/输出层 -
冻结进程
app切换到后台,并且没有其他活动时,系统在一定时间内通过状态判断,将进程id迁移到冻结的cgroup节点上。主要为了提高性能、节约资源。 -
内存ZONE
内存ZONE分为:ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHMEM、ZONE_MOVABLE
内容在enum zone_type中 -
allocstall
内存性能指标,allocstall指的是分配内存时,由于内存不足,发生了内存回收的情况。 换句话说,叫内存分配停滞或者内存分配时延 -
内存分配流程(__alloc_pages函数)
-
mm/page_alloc.c
// 申请一块2^order的连续物理内存块
struct page *__alloc_pages(gfp_t gfp, unsigned int order, int preferred_nid, nodemask_t *nodemask) {
page = get_page_from_freelist(alloc_gfp, order, alloc_flags, &ac);// 首次尝试从free快速分配
page = __alloc_pages_slowpath(alloc_gfp, order, &ac);// 内存不足进入慢分配
}
static inline struct page *__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, struct alloc_context *ac) {
// 尝试碎片整理后分配
page = __alloc_pages_direct_compact(gfp_mask, order,alloc_flags, ac,INIT_COMPACT_PRIORITY,&compact_result);
// 实在没办法了,尝试回收内存再分配
page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, ac, &did_some_progress);
}
static inline struct page *__alloc_pages_direct_reclaim(gfp_t gfp_mask, unsigned int order,unsigned int alloc_flags, const struct alloc_context *ac,unsigned long *did_some_progress){
*did_some_progress = __perform_reclaim(gfp_mask, order, ac);
page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);
}
// 回收
static unsigned long __perform_reclaim(gfp_t gfp_mask, unsigned int order,const struct alloc_context *ac){
progress = try_to_free_pages(ac->zonelist, order, gfp_mask, ac->nodemask);
}
mm/vmscan.c
unsigned long try_to_free_pages(struct zonelist *zonelist, int order,gfp_t gfp_mask, nodemask_t *nodemask){
nr_reclaimed = do_try_to_free_pages(zonelist, &sc);
}
static unsigned long do_try_to_free_pages(struct zonelist *zonelist,struct scan_control *sc){
if (!cgroup_reclaim(sc))// 记录allocstall次数
__count_zid_vm_events(ALLOCSTALL, sc->reclaim_idx, 1);
}
- numa架构
Non-Uniform Memory Access,非一致性内存访问。多核cpu架构中,将内存分成多个节点,每个核分配固定的内存节点,并且访问本地节点比访问远程节点更快。相比于传统的一致性共享内存访问,提高了多核的性能和可扩展性。
节点与zone的关系:每个节点包含多个zone。 - 内存模型
linux的物理内存管理机制将物理内存划分为三个层次来管理,依次是:Node(存储节点)、Zone(管理区)和Page(页面)。 - 尽情期待
内存优化
-
应用层
-
框架层
-
内核层
内存管理机制优化- 缺页中断
Linux已有的机制。可以做到内存的惰性分配,使得内存用起来感觉比实际大小更大。 - 安卓swap机制
1、使用zram进行压缩
2、zram快满的时候,swap到flash中的文件
swap到存储,有2种:
1、swap 分区:在存储有独立分区
2、swapfile:使用文件进行swap
- 缺页中断
-
工具
pefetto使用