大多数操作系统都采用内存回收策略来确保 在任意时间点运行的进程组都能够高效利用 所有可用的物理内存操作系统有固定数量的 所有正在运行的进程之间分配物理内存 (RAM), 无法同时满足所有需求。
简单来说,内存回收是指页面替换, 对当前用户活动并不那么重要的网页替换为 可能更重要。大多数操作系统都有一个免费网页池 这样,传入的内存分配就会快速执行,而不会阻塞 同时等待系统释放使用中的页面
Fuchsia 还采用了类似的策略,即系统会尽量保持 超过特定阈值的可用内存量。Fuchsia 使用了多种 以及内核和用户空间中的内存回收技术这个 指南介绍了这些内存回收技术的工作原理。还有紫红色 提供了一套用于分析和转储内存使用情况的工具(请参阅 内存用量分析工具)。
由分页器支持的内存逐出
用户空间文件系统使用 寻呼机 从以下位置按需下载文件中的第页: 例如磁盘文件系统使用 VMO,其页面由分页器服务在 。
在 Fuchsia 上, blobfs 是 一种不可变文件系统,使用分页器托管所有可执行文件, 以便按需填充网页当系统面临内存压力时,即 当可用内存量开始耗尽时,内核逐出页面 由 blobfs 提供支持,以回收内存。由于这些页面位于磁盘上, 可以在需要时重新提取它们
内核会跟踪所有分页器支持的内存。当可用内存不足时,它会查找 。在多个 LRU 中跟踪网页(最近 页面队列,内核后台线程定期轮替, 按年龄排序页面。另一个后台线程逐出最早的页面 加载网页队列。
当可用内存下降时,内核会调整其老化和逐出政策 采取更激进的措施,加快网页老化速度,以找出更多可逐出的网页 候选人。为了防止抖动,MRU(最近使用)中的页面 网页队列永远不会被逐出。此队列的长度因 系统流失程度。
如果系统相对安静,且内存使用量稳定,则内核 网页速度越慢,MRU 队列中会累积更多网页。另一方面 用户会循环使用几个活动,不断切换 工作集,则内核会尝试更积极地老化页面来跟上步伐。
用户空间进程也可以使用
逐出提示
影响内核逐出策略。进程可以使用 DONT_NEED
提示网页已不再使用,适合用于
被逐出。他们还可以使用 ALWAYS_NEED
来指明网页是重要网页,
不应考虑将其逐出,从而避免提取
并在用户再次访问时重新将其还原
如需详细了解逐出提示,请参阅参考文档:
zx_vmo_op_range
和
zx_vmar_op_range
。
零页重复信息删除
匿名 VMO(不受分页器支持)中的网页只能在 写入。内核使用单例零页面执行读取。甚至在 则内核会尝试删除 重新填充单例零页面以节省 内存。内核会定期扫描匿名 VMO 中的物理页面, 有机会删除重复的零网页。
页面表收回
如地址空间中所述, VMAR 层次结构可帮助内核跟踪虚拟内存到物理内存的映射。 首次访问虚拟地址时, 地址空间的 VMAR 树用于查询底层物理页面。 然后,虚拟到物理的映射会存储在硬件页表中, 供 MMU 将来查找时使用 在内存压力下 内核会回收在一段时间内未被访问的硬件页面表中的内存。 当再次需要这些映射时 它们可以通过 VMAR 树重建。
可舍弃的 VMO
用户空间进程可以 可舍弃的 VMO。 客户端可以 lock and unlock - 可舍弃 VMO 取决于是否正在使用它们。当系统处于 内核会发现已解锁并释放的可舍弃 VMO 。
示例代码(模数错误处理):
// Create a discardable VMO.
zx_handle_t vmo;
uint64_t vmo_size = 5 * zx_system_get_page_size();
zx_vmo_create(vmo_size, ZX_VMO_DISCARDABLE, &vmo);
// Lock the VMO.
zx_vmo_lock_state_t lock_state = {};
zx_vmo_op_range(vmo, ZX_VMO_OP_LOCK, 0, vmo_size, &lock_state,
sizeof(lock_state));
// Use the VMO as desired.
zx_vmo_read(vmo, buf, 0, sizeof(buf));
// Unlock the VMO. The kernel is free to discard it now.
zx_vmo_op_range(vmo, ZX_VMO_OP_UNLOCK, 0, vmo_size, nullptr, 0);
// Lock the VMO again before use.
zx_vmo_op_range(vmo, ZX_VMO_OP_LOCK, 0, vmo_size, &lock_state,
sizeof(lock_state));
if (lock_state.discarded_size > 0) {
// The kernel discarded the VMO. Re-initialize it if required.
zx_vmo_write(vmo, data, 0, sizeof(data));
} else {
// The kernel did not discard the VMO. Previous contents were preserved.
}
内存压力信号
Fuchsia 为用户空间进程提供了直接控制其 响应系统级可用内存的内存消耗客户端可以 注册接收 内存压力 信号 并根据观察到的内存压力水平采取相应措施。还有 三种内存压力等级:
名称 | 值 | 说明 |
---|---|---|
NORMAL |
1 |
内存压力等级健康状况良好。 注册的客户端可以保留缓存并分配内存 不受限制。 但是,客户端应注意不要主动在 转换回常规级别会导致内存迅速增加, 再次将该级别提升为“WARNING”。 |
警告 |
2 |
内存压力水平在一定程度上受到了限制,可能会超过 临界压力范围。 注册的客户端应优化其操作以限制内存 而不是为了获得最佳性能,例如减少缓存大小 和非必需的内存分配 客户必须注意规范他们所承担的工作量 以便回收内存,并确保它不会导致 性能下降存在一些内存压力,但还不够 以牺牲用户响应能力来回收内存是值得的。 |
危急 |
3 |
内存压力水平非常有限。 注册的客户端应丢弃所有非必需的内存,并避免 从而避免分配更多内存否则, 终止,或者系统因全局性原因而被重新启动 内存压力 如果需要,客户端可能会执行代价高昂的工作来回收内存,因为 否则,我们可能会终止您的账号。客户可能会决定 在这种情况下,性能命中是公平的权衡。 |
将内存压力信号与可舍弃的 VMO 进行比较
用户空间客户端可以选择内存压力信号和可舍弃信号 VMO 或根据其 以下是在做出选择时需要考虑的一些事项:
- 内存压力信号允许客户端执行更多操作,而不仅仅是修剪缓存。 例如,作业可以拆解其作业树中的非必需进程。 它们还可以停止某些内存密集型活动, 直到压力水平变为“正常”为止。
- 使用可舍弃的 VMO 时,用户空间客户端会放弃对何时使用 系统就会将内存释放给内核内核会决定何时释放 基于以下各种因素确定的内存:可用内存量、内存 可以通过其他方式等回收。如果客户希望 例如,控制缓存的生命周期、何时修剪什么内容、内存 可能更适合你的压力信号
- 可舍弃的 VMO 最终可能会保留其内容 整个过程为了响应而破坏 VMO 的时间更长 和内存压力信号内核会释放可舍弃的 VMO 内核对可用内存量有更全局的全局环境, 它就知道回收的数据量内核还具有 由系统自行回收内存 需要释放可舍弃的 VMO另一方面,如果用户空间 则当客户端正在响应内存压力本身时,它可能会 每次都以相同的方式截剪其所有缓存。
- 可舍弃内存还有助于内核进一步回收内存 以便系统更快地恢复。借助内存压力信号 内核之间可能会涉及一些 IPC 和调度延迟 发出信号,发出压力水平变化的信号,以及用户空间进程对此作出响应的信号。
OOM(内存不足)重新启动
可能所有内存回收策略都无法释放足够的内存 而过度耗用资源。当 之后,内核会选择在彻底关闭文件系统后重新启动 防止数据丢失当可用内存级别低于预配置的 OOM 时 阈值,就会触发 OOM 重新启动。
测试内存压力响应的工具
观察和测试内核内存回收
使用 k scanner
命令观察和测试回收技术
内核使用:由分页器支持的逐出、可舍弃的 VMO 回收、零页
去重和页面表回收。它还可用于测试网页
用于推动逐出的队列轮替 / 老化策略。在k scanner
串行控制台,查看所有可用选项:
k scanner
usage:
scanner dump : dump scanner info
scanner push_disable : increase scanner disable count
scanner pop_disable : decrease scanner disable count
scanner reclaim_all : attempt to reclaim all possible memory
scanner rotate_queue : immediately rotate the page queues
scanner reclaim <MB> [only_old] : attempt to reclaim requested MB of memory.
scanner pt_reclaim [on|off] : turn unused page table reclamation on or off
scanner harvest_accessed : harvest all page accessed information
k scanner dump
会转储页面队列的当前状态以及其他
内核用于回收的内存计数器:
k scanner dump
[SCAN]: Scanner enabled. Triggering informational scan
[SCAN]: Found 4303 zero pages across all of memory
[SCAN]: Found 8995 user-pager backed pages in queue 0
[SCAN]: Found 3278 user-pager backed pages in queue 1
[SCAN]: Found 8947 user-pager backed pages in queue 2
[SCAN]: Found 10776 user-pager backed pages in queue 3
[SCAN]: Found 3981 user-pager backed pages in queue 4
[SCAN]: Found 0 user-pager backed pages in queue 5
[SCAN]: Found 0 user-pager backed pages in queue 6
[SCAN]: Found 0 user-pager backed pages in queue 7
[SCAN]: Found 1347 user-pager backed pages in DontNeed queue
[SCAN]: Found 40 zero forked pages
[SCAN]: Found 0 locked pages in discardable vmos
[SCAN]: Found 0 unlocked pages in discardable vmos
pq: MRU generation is 12 set 10.720698018s ago due to "Active ratio", LRU generation is 6
pq: Pager buckets [8995],[3278],8947,10776,3981,0,{0},0, evict first: 1347, live active/inactive totals: 12273/25051
使用 k scanner reclaim
或 k scanner reclaim_all
测试回收内存:
k scanner reclaim_all
[EVICT]: Free memory before eviction was 7161MB and after eviction is 7290MB
[EVICT]: Evicted 33004 user pager backed pages
[SCAN]: De-duped 25 pages that were recently forked from the zero page
使用 k pmm drop_user_pt
测试页表回收:
k pmm
…
pmm drop_user_pt : drop all user hardware page tables
观察和造成内存压力
使用 k mem avail_state
命令可在
通过分配内存来达到指定的内存压力水平。这个
有助于测试系统级对内存压力的响应:
k mem avail_state
mem avail_state [step] <state> [<nsecs>] : allocate memory to go to memstate <state>, hold the state for <nsecs> (10s by default). Only works if going to <state> from current state requires allocating memory, can't free up pre-allocated memory. In optional [step] mode, allocation pauses for 1 second at each intermediate memory availability state until <state> is reached.
k mem dump
会转储当前内存压力状态。
k mem dump
watermarks: [50M, 60M, 150M, 300M]
debounce: 1M
current state: 4
current bounds: [299M, 16.0E]
free memory: 7253.5M
内存可用性状态从 0 开始编号,是一个超集 前文提到的内存压力水平 信号。
OOM
为状态 0。这是内核运行时的可用内存级别 决定重新启动系统。Imminent-OOM
是状态 1。这是一个仅用于诊断的内存级别,已设置为 与 OOM 存在微小的差异。它的唯一目的是提供一种 而现在可能为时已晚,以便安全地收集 OOM 诊断信息 在 OOM 级别收集诊断信息。如需详细了解此级别,请访问 RFC-0091。Critical
是状态 2。此级别会触发“严重错误” 内存压力信号Warning
是状态 3。这是触发警告内存的级别 压力信号Normal
是状态 4。这个级别会触发常规内存 压力信号
在上面的示例中,current state
为 4,即 Normal。
watermarks
显示了划分不同内存的内存阈值
可用性状态。以上示例中的输出显示了
阈值:
OOM: 50MB, Imminent-OOM: 60MB, Critical: 150MB, Warning: 300MB
debounce
是计算内存状态时使用的 Slack 数量或误差范围
边界。在本示例中,该大小为 1MB。
current bounds
显示适用于当前内存的可用内存边界
内存状态如果当前状态为 Normal
,则引用
watermarks
,Normal
从 300MB 的阈值开始算起。利用 1MB 去抖动
则下限为 299MBNormal
没有适用的上限
级别,此处设为UINT64_MAX
。
最后,系统上的总 free memory
目前为 7253.5MB。
使用命令 k mem avail_state X
转换为内存可用性
状态 X
,其中 X
是上述数字内存状态。
(可选)提供请求状态要保持的时长。那里
“步行”选项并在每个状态暂停时暂停
。
例如,这会触发向 Critical
内存状态的转换:
k mem avail_state 2
memory-pressure: memory availability state - Critical
pq: MRU generation is 714 set 4.144414945s ago due to "Active ratio", LRU generation is 708
pq: Pager buckets [3482],[115],317,0,199,0,{6939},0, evict first: 0, live active/inactive totals: 3597/7455
memory-pressure: set target memory to evict 1MB (free memory is 149MB)
Leaked 1817528 pages
Sleeping for 10 seconds...
[EVICT]: Free memory before eviction was 147MB and after eviction is 151MB
[EVICT]: Evicted 986 user pager backed pages
Freed 1817528 pages
memory-pressure: memory availability state - Normal
pq: MRU generation is 717 set 1.213355379s ago due to "Timeout", LRU generation is 711
pq: Pager buckets [4351],[258],149,37,0,1,{5798},0, evict first: 0, live active/inactive totals: 4609/5985
在此示例中,系统通过分配 1817528 个页面(即Critical
页面大小为 4KB)。然后发生了休眠 10 秒(默认用于保持
状态)。Critical
压力持续期间。最后,1817528
已分配的页面均已释放,内存压力下降到 Normal
。
Critical
状态转换导致一些由分页器支持的内存被逐出,
如 [EVICT]
行所示。
k mem avail_state
命令是测试内存压力的实用工具
对整个系统的响应。由于它的工作原理是
它会利用系统对其拥有的所有回收机制
处置。
以下是用于测试系统响应的其他 k mem oom
命令
特别是在 OOM 级别
mem oom [<rate>] : leak memory until oom is triggered, optionally specify the rate at which to leak (in MB per second)
mem oom hard : leak memory aggressively and keep on leaking
mem oom signal : trigger oom signal without leaking memory
包含 k mem oom
的示例输出:
k mem oom Disabling VM scanner memory-pressure: free memory is 49MB, evicting pages to prevent OOM... pq: MRU generation is 13 set 7.979442243s ago due to "Active ratio", LRU generation is 7 pq: Pager buckets [4538],[4517],3624,4606,13716,4976,{0},0, evict first: 1347, live active/inactive totals: 9055/28269 memory-pressure: found no pages to evict memory-pressure: free memory after OOM eviction is 49MB … memory-pressure: pausing for 8s after OOM mem signal [00028.317] 02811:03481> [fshost] INFO: [admin-server.cc(33)] received shutdown command over admin interface [00028.317] 02811:03481> [fshost] INFO: [fs-manager.cc(281)] filesystem shutdown initiated [00028.317] 02811:38032> [fshost] INFO: [fs-manager.cc(310)] Shutting down /data [00028.318] 12900:12902> [minfs] INFO: [minfs.cc(1471)] Shutting down [00028.340] 12900:12902> [minfs] WARNING: [src/storage/minfs/bin/main.cc(53)] Unmounted [00028.341] 02811:03481> [fshost] INFO: [admin-server.cc(39)] shutdown complete [00028.342] 02811:02813> [fshost] INFO: [main.cc(309)] terminating [00028.342] 02687:02689> [driver_manager.cm] INFO: [suspend_handler.cc(205)] Successfully waited for VFS exit completion
memory-pressure: rebooting due to OOM memory-pressure: stowing crashlog ZIRCON REBOOT REASON (OOM) Shutting down debuglog platform_halt suggested_action 1 reason 3 Rebooting...
模拟用户空间中的内存压力信号
使用 ffx profile memory signal
命令模拟
用户空间,而不会造成实际的内存压力。当目标是
测试特定用户空间进程对内存压力的响应
信号,而不改变系统的内存状态。
Signals userspace clients with specified memory pressure level. Clients can use this command to test their response to memory pressure. Does not affect the real memory pressure level on the system, or trigger any kernel reclamation tasks.
Positional Arguments: level memory pressure level. Can be CRITICAL, WARNING or NORMAL.
例如,使用 ffx profile memory signal WARNING
时,ffx
log
输出中会显示以下内容:
[00213.059579][26701][26703][memory_monitor] INFO: [pressure_notifier.cc:106] Simulating memory pressure level WARNING
请注意,此命令实际上不会分配任何内存。它只是 模拟已请求级别的一次性内存压力信号, 用户空间,而不会影响内核的内存可用性状态。因此, 不会触发任何内核内存回收,例如将分页器支持的 内存。