背景
安卓内存优化,涉及应用层、框架层、内核层。应用层侧重于使用层面,须有良好的内存使用习惯。框架层、内核层则是在aosp的基础,进一步优化系统内存管理机制。
应用层内存优化,是为了app运行更加流畅,减少crash。框架层、内核层的内存优化,主要是为了提高可用内存、app保活数量、以及系统本身的流畅度。
内存基础知识
-
应用层
避免内存泄漏、OOM、延迟使用对象等,详情略。 -
框架层
-
进程ADJ
系统运行,内存过低时,根据进程优先级回收内存。ADJ代表进程优先级。
Linux 内核 通过 proc文件系统,暴露 /proc/[pid]/oom_score_adj文件,来允许其他程序修改指定进程的优先级。值越小进程越重要。内存紧张时,系统会遍历所有进程,以确定杀死哪个进程,回收内存,此时会读取 oom_score_adj 这个文件的值。
ProcessList.java中预定义了 oom_score_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){
// 首次尝试从free快速分配
page = get_page_from_freelist(alloc_gfp, order, alloc_flags, &ac);
// 内存不足进入慢分配
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){
// 记录allocstall次数
if (!cgroup_reclaim(sc))
__count_zid_vm_events(ALLOCSTALL, sc->reclaim_idx, 1);
}