android 预优化oat_Android内存优化大盘点

内存优化是性能优化的重头戏,因此这部分也花了很多时间来梳理。老规矩,先上大纲:

ebb4866a7bc38342a54f72660c731240.png

1.1 Android内存管理框架:

ac826c0a9476785f19c91ed9cd9f65ed.png

这里针对上图进行简单描述:

1)物理地址与虚拟地址:

虚拟内存是程序和物理内存之间引入的中间层,目的是解决直接使用物理内存带来的安全性问题、超过物理内存大小需求无法满足等等问题。

而Linux的内存管理就是建立在虚拟内存之上的。虚拟地址与物理地址通过页表建立映射关系,CPU通过MMU(Memory Management Unit :内存管理单元)访问页表来查询虚拟地址对应的物理地址。虚拟地址分为内核空间和用户空间,它们对应的虚拟地址分别为进程共享和进程隔离的。

2)内核空间内存管理:

内核把page作为内存管理的基本单位。对特性不同的page又以zone来做划分,zone又由node来管理。

主要关注的区有3个:

描述
ZONE_DMA 直接内存访问,无需映射
ZONE_NORMAL 一一对应映射页
ZONE_HIGHMEM 动态映射页

每个zone中内存的组织形式是基于buddy伙伴算法,把空闲的page以2的n次方为单位进行管理。因此Linux最底层的内存申请都是以2的n次方为单位来申请page的。

Buddy伙伴算法以产生内部碎片为代价来避免外部碎片的产生。Linux针对大内存的物理地址分配,采用Buddy伙伴算法,如果是针对小于一个page的内存,频繁的分配和释放,则不宜用Buddy伙伴算法,取而代之的是Slab。

Slab是为频繁分配/释放的对象建立高速缓存。

3)用户空间内存管理:

用户空间主要分两部分,一个是面向C++的native层,一个是基于虚拟机的java层。

native内存划分:

  • Data 用于保存全局变量

  • Bss 用于保存全局未初始化变量

  • Code 程序代码段

  • Stack 线程函数执行的内存

  • Heap malloc分配管理的内存

java基于虚拟机的内存划分:

  • Program Counter Register 它是一个指针,指向执行引擎正在执行的指令的地址。

  • VM stack 基于方法中的局部变量,包括基本数据类型以及对象引用等。

  • Native Method Stack 针对native方法,功能与虚拟机栈一致。

  • Method Area 虚拟机加载的类信息、常量、静态变量等。

  • Heap 对象实体。

20dd2169472967e68c6eb4cdedeeacd5.png

1.2 linux内存分配与回收

内存分配:

在调用alloc_page或者alloc_pages等接口进行一次内存分配时,最后都会调用到__alloc_pages_nodemask函数,这个函数是内存分配的心脏,对内存分配流程做了一个整体的组织。该流程牵涉到的分配过程有两个:

快速内存分配:是get_page_from_freelist函数,通过low阀值从zonelist中获取合适的zone进行分配,如果zone没有达到low阀值,则会进行快速内存回收,快速内存回收后再尝试分配。

慢速内存分配:当快速分配失败后,也就是zonelist中所有zone在快速分配中都没有获取到内存,则会使用min阀值进行慢速分配,在慢速分配(slow path)过程中主要做三件事,异步内存压缩、直接内存回收以及轻同步内存压缩,最后视情况进行oom分配。并且在这些操作完成后,都会调用一次快速内存分配尝试获取页框。

内存回收:

内存回收是以zone为单位进行的(也会以memcg为单位,这里不讨论这种情况),而系统判断一个zone需不需要进行内存回收是由水线watermark来判断的。

high 当zone的空闲页框数量高于这个值时,表示zone的空闲页框较多,不需要再继续进行内存回收。

low 快速分配的默认阀值,在分配内存过程中,如果zone的空闲页框数量低于此阀值,系统会对zone执行快速内存回收。

min 在快速分配失败后的慢速分配中会使用此阀值进行分配,如果慢速分配过程中使用此值还是无法进行分配,那就会执行直接内存回收和快速内存回收。

当linux系统内存压力就大时,就会对系统的每个压力大的zone进程内存回收,它针对三样东西进程回收:slab、lru链表中的页、buffer_head。

这里主要看lru链表中的页是怎么回收的。lru链表主要用于管理进程空间中使用的页,类型分为:文件页、匿名页、shmem页。

文件页(file-backed page):有文件背景页面。可以直接和硬盘对应的文件进行交换。

匿名页(anonymous page):无文件背景页面。如进程堆、栈、数据段使用的页等,无法直接跟磁盘交换,但是可以跟swap区进行交换。

mmap页(tmpfs/shmem的page):它具有文件的属性,能够像操作文件一样去操作它。但是它无文件背景,因此也有匿名页属性,内核在内存紧缺时不能简单的将page从它们的page cache中丢弃,而需要swap-out。

f214d61e7e971f6def4c4b5f9b544fd8.png

三种文件页回收对比,图片出处:[linux内核tmpfs/shmem浅析]

Lru链表回收算法

Lru链表有5个双向链表:

LRU_INACTIVE_ANON、LRU_ACTIVE_ANON、LRU_INACTIVE_FILE、LRU_ACTIVE_FILE、LRU_UNEVICTABLE。

老化过程:将不处于lru链表的新页放入到lru链表中->将处于活动lru链表的页移动到非活动lru链表->将非活动lru链表中的页移动到非活动lru链表尾部->回收页然后将页从lru链表中移除。

页回收方式

  • 页回写:文件页保存的数据与磁盘中文件对应的数据不一致,则认定此文件页为脏页,需要先将此文件页回写到磁盘中对应数据所在位置上,然后再将此页作为空闲页框释放到伙伴系统中。

  • 页交换:不经常使用的匿名页,将它们写入到swap分区中,然后作为空闲页框释放到伙伴系统。

  • 页丢弃:文件页中保存的内容与磁盘中文件对应内容一致,说明此文件页是一个干净的文件页,就不需要进行回写,直接将此页作为空闲页框释放到伙伴系统中。

当内存紧张时,优先换出无脏数据的page cache(文件页包含page cache),直接丢弃。其次才是匿名页和有脏数据的文件页的回收。遵循URL老化规则。通过Swappiness来确定更倾向于回收哪种更多一点,swappiness越大,越倾向于回收匿名页,反之越倾向于回收文件页。

内存回收手段

因为在不同的内存分配路径中,会触发不同的内存回收方式,内存回收针对的目标有两种,一种是针对zone的,另一种是针对一个memcg的,而这里我们只讨论针对zone的内存回收,个人把针对zone的内存回收方式分为三种,分别是快速内存回收、直接内存回收、kswapd内存回收。

快速内存回收:处于get_page_from_freelist函数中,在遍历zonelist过程中,对每个zone都在分配前进行判断,如果分配后zone的空闲内存数量 < 阀值 + 保留页框数量,那么此zone就会进行快速内存回收,即使分配前此zone空闲页框数量都没有达到阀值,都会进行此zone的快速内存回收。注意阀值可能是min/low/high的任何一种,因为在快速内存分配,慢速内存分配和oom分配过程中如果回收的页框足够,都会调用到get_page_from_freelist函数,所以快速内存回收不仅仅发生在快速内存分配中,在慢速内存分配过程中也会发生。

直接内存回收:处于慢

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值