Linux内存回收入口

Android内存回收:GC、kswapd 和 mm_vmscan_direct_reclaim概述 Android内存管理机制解析:GC、kswapd与direct reclaim 摘要:本文深入分析了Android系统中的三种内存管理机制。Java/ART GC负责回收Java堆内存,而kswapd是内核后台异步回收线程。当内存不足时,应用线程可能被迫执行direct reclaim(mm_vmscan_direct_reclaim),在内核态同步回收内存。这种现象不同于GC的全局暂停,而是特定线程的阻塞式回收。文章详细解释了为何在有GC和kswapd的情况下仍会发生direct reclaim,指出主 阅读详情
概述

内存回收主要是有kswapd异步回收和direct reclaim同步回收两种入口,其中逻辑非常复杂,本文主要只概要描述不同回收场景下内核设计的主要思想,源码细节不同版本有不少区别,具体的分析后续会有专门的文章分析。

 页面回收常识:

  • 页面扫描回收时才意味着老化。
    • 一个页面被访问之后,相应的access标记会一直打在那里,直到这个页面被扫描。LRU里面的时间流逝跟自然时间是没有关系的,扫描才是推动历史车轮前进的动力。而扫描又是由于达不到balanced而被触发的,可见页面老化的速度跟系统中内存的紧缺程度是相关的。内存紧缺的时候,1分钟前才被访问过的页面可能都不会被视作活跃;反过来,如果内存不紧缺,长期不需要进行回收,那么几小时前访问过的页面又可能都会被视作活跃,并且在这段时间内被访问过一次和被访问过多次的页面会被同等对待(access是标记而不是计数);
 kswapd后台异步回收

1)何时唤起kswapd:

  • 如果内存低于low watermark系统会唤醒kswapd进行内存回收
  • alloc_page申请内存时候,先使用low watermark进行快速内存申请,如果失败了就会进入慢路径(也就是说free page < low),这个时候会主动wakeup kswapd,同时使用min watermark申请内存。

2)kswapd何时停止,回收(一个for循环)的频率如何

     核心思路:既要高效回收page,又不能过度消耗CPU。

  • 如上图kswapd如果判定pgdat_balanced(判定order和high watermark是否满足),那么就先进入“浅睡眠”(schdule_timeout(100)),避免马上陷入full sleep(TASK_INTERRUPTIBLE sleep),只能等到direct reclaim唤醒了,影响了内存分配性能,这里也可以看出kswapd的作用就是要尽量保持内存在high watermark,但是又要避免for(;;)过度循环导致cpu太高。
  • 如果kswapd回收后满足pgdat_balanced陷入睡眠后,将wakeup_kcompactd,做内存compact。
  • 如果kswapd一直回收而始终不满足pgdat_balanced,那么kswapd prepare_kswapd_sleep返回false,意味者kswapd不能休息要一直回收。

3)kswapd 回收page目标数量是多少?

  • 目标:free page要满足high watermark;而kswapd又是从低于low watermark开始回收的,故源码设置sc.nr_to_reclaim尽量比较大能满足上述目标,如下:
	for (z = 0; z <= sc->reclaim_idx; z++) {
		zone = pgdat->node_zones + z;
		if (!managed_zone(zone))
			continue;
        //high_wmark_pages返回的high watermark值
		sc->nr_to_reclaim += max(high_wmark_pages(zone), SWAP_CLUSTER_MAX);
	}

 注意:虽然kswapd的sc.nr_to_reclaim虽然设置的回收目标挺大的,但是真正执行shrink回收页面的时候(实际上指shrink_list)一次回收的单位最大是SWAP_CLUSTER_MAX(32)个pages。

4)kswapd回收的方向(zone)和 order?

  • kswapd回收方向和alloc_pages方向相关尽力避免锁竞争。
    • alloc_pages方向是从high zone-> low zone;kswapd反之
  • kswapd会从alloc_pages拿到目标zone(prefered zone)和order,然后按zone 0 -> prefered zone顺序回收(比如prefered zone是1,那么只回收0 - 1zone,不要浪费时间去收回zone 2)
  • 上面大概提到判定zone是否平衡的条件有考虑order,可参见源码pgdat_balanced函数,平衡不仅仅是要free pages > high watermark,同时至少要有2^order连续的物理内存才可。

5)kswapd's scan control priority优先级设置

  • kswapd开始以DEF_PRIORITY(12)的优先级开始扫描
  • 调用函数balance_pgdat在do{}循环中进行回收,如果一轮循环回收后没有balance,那就不断将priority提升,增加一轮scan page数量;如果priority最高后还没有balance,根据watermark_boots情况可以看是否wakeup_kcompactd。

direct reclaim
  • 快速路径申请:
    • 以ALLOC_WMARK_LOW申请内存,zone水位不满足条件(即free pages < low),node_reclaim触发回收,此过程不能够引入I/O开销,因此node_reclaim不可执行脏页回写和页面取消映射(unmap),不过swap操作这种zram压缩还是允许的。
    • 注意:默认android这种非NUMA不走node_reclaim逻辑。
static int __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order)
{
    ...
	struct scan_control sc = {
		.nr_to_reclaim = max(nr_pages, SWAP_CLUSTER_MAX),
		.gfp_mask = current_gfp_context(gfp_mask),
		.order = order,
		.priority = NODE_RECLAIM_PRIORITY,
		.may_writepage = !!(node_reclaim_mode & RECLAIM_WRITE),
		.may_unmap = !!(node_reclaim_mode & RECLAIM_UNMAP),
		.may_swap = 1,
		.reclaim_idx = gfp_zone(gfp_mask),
	};
    ...
}

my_writepage = 0:代表不能进行页面回写落盘

my_unmap = 0 : 不能进行try_to_unmap(page)等unmap解除页表映射。

may_swap = 1;可以进行页面swap操作,android上比如可以机型zram匿名页压缩。

priority : 回收优先级是4,即最多尝试调用shrink_node进行回收的次数为4次,或者当回收到的页数达到需要分配的内存页数停止。

nr_to_reclaim:请求分配页面和SWAP_CLUSTER_MAX的最大值,即最少也要扫描32个page,否则一次shrink性价比太低了。

慢路径申请:

如果快速路径回收页面后依旧分配内存失败,则会进入到慢速分配路径,慢速分配会尝试从所有zone中以WMARK_MIN阀值分配内存页。在慢速分配路径中,会再次尝试快速路径回收,未成功则调用__alloc_pages_direct_reclaim进行内存回收。允许mapped anonymous page回收,且如果laptop_mode不为真,则允许脏文件页写回。laptop_mode通过/proc/sys/vm/laptop_mode控制,为0允许直接内存回收对脏页进行回写操作。try_to_free_pages每次回收页面最大值为32(SWAP_CLUSTER_MAX),priority为12,shrink_node最多会被调用12次。

unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
                gfp_t gfp_mask, nodemask_t *nodemask)
{
    unsigned long nr_reclaimed;
    struct scan_control sc = {
        .nr_to_reclaim = SWAP_CLUSTER_MAX,
        .gfp_mask = current_gfp_context(gfp_mask),
        .reclaim_idx = gfp_zone(gfp_mask),
        .order = order,
        .nodemask = nodemask,
        .priority = DEF_PRIORITY,
        .may_writepage = !laptop_mode,
        .may_unmap = 1,
        .may_swap = 1,
    };
    ...
}

static inline struct page *
__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
						struct alloc_context *ac)
{
	bool can_direct_reclaim = gfp_mask & __GFP_DIRECT_RECLAIM;
    ...

retry_cpuset:
    ...
    //添加ALLOC_WMARK_MIN
	alloc_flags = gfp_to_alloc_flags(gfp_mask);
    ...
}


static inline unsigned int
gfp_to_alloc_flags(gfp_t gfp_mask)
{
	unsigned int alloc_flags = ALLOC_WMARK_MIN | ALLOC_CPUSET;
    ...

	return alloc_flags;
}

调用栈:

#0  shrink_node (pgdat=0xffff888007fda000, sc=0xffff888005767450) at mm/vmscan.c:2672
#1  0xffffffff81356a4a in shrink_zones (sc=<optimized out>, zonelist=<optimized out>) at mm/vmscan.c:2971
#2  do_try_to_free_pages (zonelist=<optimized out>, sc=0xffff888005767450) at mm/vmscan.c:3026
#3  0xffffffff81358cec in try_to_free_pages (zonelist=0xffff888007fdb400, order=0, gfp_mask=<optimized out>, nodemask=<optimized out>) at mm/vmscan.c:3265
#4  0xffffffff813bcd27 in __perform_reclaim (ac=<optimized out>, ac=<optimized out>, order=<optimized out>, gfp_mask=<optimized out>) at mm/page_alloc.c:4274
#5  __alloc_pages_direct_reclaim (did_some_progress=<optimized out>, ac=<optimized out>, alloc_flags=<optimized out>, order=<optimized out>, gfp_mask=<optimized out>) at mm/page_alloc.c:4295
#6  __alloc_pages_slowpath (gfp_mask=<optimized out>, order=<optimized out>, ac=0x0 <fixed_percpu_data>) at mm/page_alloc.c:4699
#7  0xffffffff813bdf9d in __alloc_pages_nodemask (gfp_mask=1125578, order=0, preferred_nid=<optimized out>, nodemask=<optimized out>) at mm/page_alloc.c:4915
#8  0xffffffff813e0637 in alloc_pages_current (gfp=1125578, order=0) at ./include/linux/topology.h:88
#9  0xffffffff8132b0b5 in alloc_pages (order=<optimized out>, gfp_mask=<optimized out>) at ./include/linux/gfp.h:545
#10 __page_cache_alloc (gfp=<optimized out>) at mm/filemap.c:957
#11 __page_cache_alloc (gfp=1125578) at mm/filemap.c:942

具体回收的过程以及判定哪些页面怎么处理的细节后面文章分析。

Android 操作系统的内存回收机制 Android APP 的运行环境 Android 是一款基于 Linux 内核,面向移动终端的操作系统。为适应其作为移动平台操作系统的特殊需要,谷歌对其做了特别的设计与优化,使得其进程调度与资源管理与其他平台的 Linux 有明显的区别。主要包含下面几个层次: Application Framework Application Framework 将整个操作系统分隔成两个部 阅读详情

相关推荐

Linux内存管理(22):buddy 分配器前篇

在上一文buddy 系统中,简单介绍了 buddy 系统的初始化过程以及其原理。我们知道 buddy 系统是操作系统中常用的一种动态存储管理方法,在用户提出申请时,分配一个大小合适的内存块给用户,并在用户释放内存块时回收。在buddy 系统中,内存块(page block)的大小是2 的 order 次幂个pages。Linux 内核中 order 的最大值用 MAX_ORDER 来表示,通常是11,也就是最大的物理内存块可以达到pages(4MB)。zone 中的 free_area 数组分别管理。

私房菜 1505

Linux内存直接回收(Direct Reclaim)机制详解

然而,如果后台回收的速度跟不上内存消耗的速度,或者由于内存碎片等原因无法有效回收足够的内存,那么当新的内存分配请求到来且可用内存低于某个临界点时,直接回收机制就会被激活。当系统可用内存降至极低水平,且一个进程尝试分配新的内存时,内核会触发直接回收,这是一个同步的过程,意味着触发内存分配的进程会暂停执行,直接参与到回收内存的操作中。然而,在某些情况下,在某些极端情况下,如果系统触发了直接回收,但是没有任何可回收的页面(例如,没有启用交换空间,并且所有内存都被不可回收的进程占用),那么系统可能会发生挂起。

weixin_39145568的博客 2277

用树莓派Pico低成本更新USB-C PD控制器固件实战指南

固件更新是嵌入式系统开发与维护中的核心环节,它直接决定了硬件设备的功能、性能与兼容性。其原理通常是通过特定的通信接口(如SWD、JTAG、UART或USB)与设备内部的Bootloader建立连接,将新的程序代码安全写入非易失性存储器。在USB Power Delivery(PD)和Type-C生态中,固件更新技术尤为重要,它能修复协议握手Bug、适配新设备并提升供电策略的灵活性。本文聚焦于一个典型的工程实践场景:如何利用广泛普及且成本极低的树莓派Pico开发板,作为USB-C协议到I²C协议的转换桥,实现

weixin_27869497的博客 455

当内存紧张时出现mm_vmscan_direct_reclaim 直接回收,需要对其分析

Linux内核内存管理中的mm_vmscan_direct_reclaim路径负责同步直接页面回收,当系统空闲内存低于低水位线时触发。该过程会阻塞申请进程,通过try_to_free_pages()函数执行回收,可能导致性能抖动。关键监控指标包括pgscan_direct、pgsteal_direct和allocstall。调优建议包括调整lowmem_reserve_ratio参数优化内存域保留页比例。try_to_free_pages()作为核心回收函数,通过scan_control结构体控制回收行为,

wydaicls的博客 1020

用Arduino和MAX30102做个心率血氧仪,从硬件连线到算法调试全流程避坑

本文详细介绍了如何使用Arduino和MAX30102传感器构建高精度心率血氧仪,涵盖硬件连线、软件配置、信号处理及算法优化全流程。通过实战案例和避坑指南,帮助开发者快速实现临床级精度的健康监测设备,适用于可穿戴设备和医疗健康领域。

weixin_26897585的博客 383

【MDC开发者实战】多传感器融合算法在自动驾驶中的配置与优化

本文详细介绍了华为MDC平台在自动驾驶中多传感器融合算法的配置与优化实践。通过前融合与后融合技术,结合摄像头、激光雷达和毫米波雷达的数据,提升感知精度与可靠性。文章还分享了ARXML配置、标定技巧、算法调优及性能优化等实战经验,帮助开发者高效实现自动驾驶系统的传感器融合。

weixin_29214199的博客 514

linux 内存源码分析工具,linux内存源码分析

直接内存回收中的等待队列内存回收详解见linux内存源码分析 - 内存回收(整体流程),在直接内存回收过程中,有可能会造成当前需要分配内存的进程被加入一个等待队列,当整个node的空闲页数量满足要求时,由kswapd唤醒它重新获取内存。这个等待队列头就是node结点描述符pgdat中的pfmemalloc_wait。如果当前进程加入到了pgdat->pfmemalloc_wait这个等待队列...

weixin_42510243的博客 276

基于VS2012 C#调用bartender自动打印条形码

GitHub:https://github.com/sanjaywu/BarcodePrintTool 一、在引用添加Interop.BarTender.dll 1、在资源解决方案的引用这里,右击选择“添加引用”: 2、按照如下步骤添加Interop.BarTender.dll引用: 3、添加完成 二、使用bartender工具设置打印模板 1、页面大小设置: 2、条码设置: 三、代码...

Sanjay_Wu的博客 5955

linux内存管理(十二)-直接页面回收

__alloc_pages_direct_reclaim函数位于mm/page_alloc.c文件中: 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) { struct page

jianjian的博客 2134

保姆级教程:在mmdetection中手动计算并可视化P、R、F1值(附完整代码)

本文提供了一份详细的mmdetection教程,指导开发者如何手动计算并可视化精确率(P)、召回率(R)和F1值。通过完整的代码示例和清晰的步骤说明,帮助用户深入理解目标检测模型的性能评估方法,特别适合需要精细分析模型表现的计算机视觉研究人员和工程师。

weixin_29038303的博客 399

宇瞳杯安防镜头设计

宇瞳杯安防镜头设计

weixin_72050316的博客 1507

唠嗑内核——内存回收漫谈

还是刚才说的唯一标识,也就是inode。对于一个新page,第一次访问则置PG_referenced,表示访问过(即inactive,unreferenced -> inactive,referenced),但偶尔访问一次不足以成为Active,因此如果在PG_referenced已置的情况下再次访问改Page,则升级成Active,置PG_active,同时取消PG_referenced(即inactive,referenced -> active,unreferenced),以便后续统计其访问情况。

十寸甫先生的博客 826

16、Arduino MEGA 2560 固件与电路详解

本文详细介绍了Arduino MEGA 2560的固件编程与电路设计,涵盖ATmega16U2和ATmega2560的熔丝配置、stk500v2引导加载程序的备份与恢复方法、使用Microchip Studio通过ICSP烧录固件的步骤,以及debugWIRE和JTAG调试接口的选择与配置。同时提供了电路块图解析、组件位置参考及常见问题解决方案,帮助用户深入理解硬件结构并进行高效开发与调试。

swift5iosmith的博客 194

Linux内存管理 (13)回收页面

https://www.cnblogs.com/arnoldlu/p/8335487.html 专题:Linux内存管理专题 关键词:LRU、活跃/不活跃-文件缓存/匿名页面、Refault Distance。 页面回收、或者回收页面也即page reclaim,依赖于LRU链表对页面进行分类:不活跃匿名页面、活跃匿名页面、不活跃文件缓存页面、活跃文件缓存页面和不可回收页面。 内存紧张时优先换出文...

hbcbgcx的博客 1072

使用pytorch-superpoint与pytorch-superglue项目实现训练自己的数据集

superpoint与superglue的组合可以实现基于深度学习的图像配准,官方发布的superpoint与superglue模型均基于coco数据训练,与业务中的实际数据或许存在差距,为此实现基于开源的pytorch-superpoint与pytorch-superglue项目实现训练自己的数据集。然而,在训练pytorch-superpoint有诸多细节需要优化,特此整理成技术文档。本文档描述了实验pytorch-superpoint与pytorch-superglue项目训练自己的图像配准模型。

a486259的博客 9503

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Android 操作系统的内存回收机制[转]

转自:http://www.ibm.com/developerworks/cn/opensource/os-cn-android-mmry-rcycl/ Android APP 的运行环境 Android 是一款基于 Linux 内核,面向移动终端的操作系统。为适应其作为移动平台操作系统的特殊需要,谷歌对其做了特别的设计与优化,使得其进程调度与资源管理与其他平台的 Linux 有明显的区别。主...

weixin_30273763的博客 92

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

线程创建&回收:pthread_creat和pthread_join

pthread_create()是一个POSIX线程函数,用于创建一个新线程。void *arg);这个函数创建新线程的主要步骤为:1. 检查传入的线程属性attr是否正确,如果为空则使用默认属性。2. 将start_routine函数和参数arg封装为线程任务。3. 系统将分配资源为新线程创建环境,并保存线程ID到thread参数。4. 新创建的线程会调用start_routine(arg)函数开始执行线程任务。5. 如果资源分配成功,pthread_create()返回0,否则返回错误码。

pl0020的博客 1967

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Linux ELF程序执行全过程

内核读取 ELF Header根据 Program Header 加载 segment(text/data/bss)加载动态链接器 ld-linux.so加载共享库(.so)重定位符号执行全局构造函数进入 _start调用 main()main 返回后调用全局析构exit() 退出进程。

weixin_45481496的博客 454

linux 内存 管理 源码,【Linux内存源码分析】伙伴管理算法(5)

前面已经分析了伙伴管理算法的释放实现,接着分析一下伙伴管理算法的内存申请实现。伙伴管理算法内存申请和释放的入口一样,其实并没有很清楚的界限表示这个函数是入口,而那个不是,所以例行从稍微偏上一点的地方作为入口分析。于是选择了alloc_pages()宏定义作为分析切入口:【file:/include/linux/gfp.h】#define alloc_pages(gfp_mask, order) \...

weixin_36168927的博客 280

ptmalloc源码分析 - ptmalloc的初始化实现(01)

前几年阅读过华庭的《glibc内存管理ptmalloc源代码分析》文章,并做过一篇笔记 今年打算重点阅读一下glibc里面,malloc部分的具体实现机制。 ptmalloc简介 Linux早期的版本,是由Doug Lea实现的,但是早期的版本有一个问题,就是没办法处理多线程下并发分配和回收的高效和正确性。Wolfram Gloger在Doug Lea的基础上改进使得Glibc的malloc可

自娱自乐的代码人 6万+

memcg 水位线

触发超过high,就是执行一个回收,由内核直接回收/返回用户态的时候回收,不管是哪种,对用户态都有延迟。从中可以看出,开始时,并不会去扫描low以下的cgroup,尽量保护他们,但是实在回收不上来,那没办法了,还是得回收。memcg计数入口是try_charge_memcg。注意,cgroup级别的水位线与全局的水位线是反过来的,全局水位线是指reserve多少,cgroup水位是指用了多少。例如,一个cgroup,设置max=10G, high是8G,low是4G,min是2G,这个代表什么意思呢?

fangundedongdong的博客 367

do_try_to_free_pages

/*  * This is the main entry point to direct pagereclaim.  *  * If a full scan of the inactive list fails tofree enough memory then we  * are "out of memory" and somethingneeds to be killed.  *

星空探索 686

Linux内存回收机制

Linux内存回收机制 内核之所以要进行内存回收,主要原因有两个: 内核需要为任何时刻突发到来的内存申请提供足够的内存,以便cache的使用和其他相关内存的使用不至于让系统的剩余内存长期处于很少的状态。 当真的有大于空闲内存的申请到来的时候,会触发强制内存回收。 在不同的内存分配路径中,会触发不同的内存回收方式,内存回收针对的目标有两种,一种是针对zone的,另一种是针对一个memcg的,把...

abc593043305的博客 1万+

飞凌嵌入式ElfBoard-进程之什么是进程

进程是正在执行(已经开始执行但还没终止的)的程序实例,执行程序时,内核会将程序载入虚拟内存,为程序变量分配空间,建立内核记账(bookkeeping)数据结构,以记录与进程有关的各种信息(比如,进程ID,用户ID,组ID以及终止状态等)。进程是一个活动的实体,可以看作是程序在某一时刻的状态,包括程序计数器、栈、堆和数据段等。程序是一个静态的代码集合,是一组指令的集合,用于执行特定的任务。操作系统在内核中为新进程分配一个唯一的进程 ID,并复制父进程(Shell)的内存空间和上下文,形成一个新的执行环境。

2301_76587520的博客 342

Linux内存管理(九): 页面回收

page reclaim ##1. 内存回收 系统在运行一段时间后,内存逐渐的被分配过去, 空闲内存会越来越少,为了保证之后的程序有足够的内存可用, linux 内核会通过“page reclaim”机制 回收一部分内存。 1.1 页面回收主要会做哪些动作 将count = 0 1.1 哪些page可以被回收 count = 0? page cache? swap? 进程? 1.2 什么时候触发内存回收? 1.3 页面回收算法 1.4 水位 内核实现 2.1 数据结构 lru, scan contrl

Hober 3737

linux 内存回收lru算法代码注释1

node_reclaim->__node_reclaim->shrink_node,这里调用shrink_node_memcgs回收后,要判断下是否分配内存速度比回收速度快太多,这种情况可能会尝试停一段时间等回收完成(reclaim_throttle),然后再看是否需要继续回收(should_continue_reclaim)。shrink_inactive_list会将部分页回收、送入swap区,或迁移到其它numa node上。

qq_37517281的博客 1644
上一篇: 搞懂sparsemem稀疏内存模型
下一篇: linux页框回收之shrink_node函数源码剖析
OS技术解密
博客等级 码龄16年 3760粉丝 189原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值