帮你快速理解、总结文档立即下载

内存管理调优

最近更新时间:2026-08-24 18:27:31
我的收藏

背景信息

内存调优是指在具体业务场景下(如:数据库(Redis、MySQL)、AI 推理、虚拟机等大内存业务场景),针对内存分配策略、隔离与保护机制的系统性调整,通过合理调优,可以在不修改应用代码的情况下显著提升大内存应用的性能,并防止单进程内存异常拖垮整机。典型场景下的常见问题包括:
内存访问效率低:默认 4KB 页面导致 TLB miss 频发,大内存应用的有效内存访问延迟偏高。
用户态分配无法利用大页:glibc malloc 默认不使用大页,即使系统开启了透明大页(THP),也无法对用户态动态分配生效。
核心业务可能被误杀:系统整体内存不足时,OOM Killer 按 oom_score 得分选择进程终止(通常是内存占用最大的进程),不考虑进程的业务重要性。
单容器内存泄漏拖垮整机:单个容器的内存持续增长可能耗尽整机内存,导致其他无关业务被连带影响。
TencentOS Server 4 基于 Linux 6.6 内核,提供透明大页(THP)、显式大页(HugeTLB)、glibc hugetlb 环境变量(进程级大页控制)、cgroup v2 内存限制和 PSI 内存压力指标等机制。

注意事项

了解并确认系统内存大小(通过 free -h 查看),大页预分配需根据物理内存规划。
生产环境调优前,建议先在测试环境验证,尤其是 THP 和 HugeTLB 配置可能影响业务延迟,透明大页设为 always 可能对延迟敏感型业务(如数据库)造成偶发延迟抖动,推荐容器混部场景使用 madvise,由应用显式请求大页。
glibc hugetlb 环境变量需要 glibc 2.35 及以上版本支持,TencentOS Server 4 默认满足。低版本 glibc 会静默忽略此变量。
使用系统默认大页(hugetlb=2)或自定义大页(hugetlb=<N>)时,必须先预分配 nr_hugepages,否则 malloc 会在运行时获取大页失败。
大页内存一旦预分配就从普通内存池中扣除,未使用的大页不能被其他普通应用使用,需根据业务实际内存需求规划预留数量。
自定义大页值(hugetlb=<N>)的单位是字节,设为 1GB 大页时应写 1073741824,不是 1G
oom_score_adj 设为 -1000 会完全禁止该进程被 OOM Killer 选中,但如果系统整体内存耗尽,OOM Killer 可能转而杀掉其他关键进程。更推荐用 cgroup memory.max 限制单进程内存。
cgroup memory.high 不应设置过低于 memory.max,否则会导致频繁的内存回收,影响应用性能。建议 memory.high 设为 memory.max 的 80%~90%。
OOM Killer 的默认行为是杀掉 cgroup 内 oom_score 最高的进程(通常是内存占用最大的),不会考虑进程的业务重要性。核心业务应通过 oom_score_adj 或 cgroup 隔离保护。
本文使用的 OOM 日志示例,可能因内核版本变化略有差异,但字段含义相同。

接口与参数说明

透明大页(THP)配置接口

接口文件
取值范围
默认值
读写含义
/sys/kernel/mm/transparent_hugepage/enabled
always/madvise/never
madvise
读写:设置透明大页使用策略。
always 对所有进程启用;
madvise 仅对显式请求的区域启用;
never 禁用
/sys/kernel/mm/transparent_hugepage/defrag
always/defer/defer+madvise/madvise/never
madvise
读写:设置内存碎片整理策略。
always 持续整理;
defer 延迟整理;
defer+madvisemadvise 区域延迟整理;
madvise 仅整理显式请求区域;
never 不整理
/sys/kernel/mm/transparent_hugepage/hpage_pmd_size
只读
2097152(2MB)
读:查看透明大页大小(字节)

glibc HugeTLB 环境变量

环境变量
取值
含义
GLIBC_TUNABLES=glibc.malloc.hugetlb=0
0
不使用大页
GLIBC_TUNABLES=glibc.malloc.hugetlb=1
1
使用透明大页(需 THP 设为 madvise
GLIBC_TUNABLES=glibc.malloc.hugetlb=2
2
使用系统默认大页(需预分配 nr_hugepages
GLIBC_TUNABLES=glibc.malloc.hugetlb=<N>
N>2
使用指定大小的大页(需预分配对应规格)
说明:
单位字节,需等于系统支持的大页字节数(如 2097152 或 1073741824)。可取值以 /sys/kernel/mm/hugepages 目录下实际存在的规格为准,写入不支持的值时 malloc 会回退到普通内存分配。

显式大页(HugeTLB)配置接口

接口文件
取值
默认值
说明
/proc/meminfoHugepagesize
只读
2048 kB(2MB)
系统默认大页大小
/proc/meminfoHugePages_Total
只读
0
已分配的大页总数
/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
正整数
0
读写:2MB 大页预留数量
/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
正整数
0
读写:1GB 大页预留数量

cgroup v2 内存控制接口

接口文件
取值范围
默认值
读写含义
memory.max
字节数或 max
max
读写:内存硬限制,超过后触发 OOM
memory.high
字节数或 max
max
读写:内存软限制,超过后触发回收
memory.swap.max
字节数或 max
max
读写:swap 使用上限
memory.reclaim
字节数
只写:写入字节数触发主动回收
memory.stat
只读
读:分页级内存使用统计
memory.pressure
只读
读:PSI 内存压力指标
memory.oom.control
0/1
0
读写:是否禁用 cgroup 内 OOM Killer

OOM Killer 关键参数

参数
位置
取值
说明
/proc/<pid>/oom_score_adj
procfs
-1000 ~ 1000
调整进程被 OOM Killer 选中概率,-1000 表示禁止杀
/proc/<pid>/oom_score
procfs
只读
当前 OOM 评分(越高越容易被杀)
vm.panic_on_oom
sysctl
0/1
0=调用 OOM Killer(默认)
1=直接 panic
vm.oom_kill_allocating_task
sysctl
0/1
0=正常选择流程(默认),内核按 oom_score 得分选择终止目标(通常是内存占用最大的进程),不一定是触发分配失败的进程
1=直接终止发起本次内存分配的进程,跳过 oom_score 打分

配置示例

启用透明大页

注意:
修改 THP 策略立即对全系统生效,可能影响延迟敏感型业务(详见注意事项),生产环境建议先在测试环境验证。
请您根据业务类型,选择透明大页的模式:
# 确认透明大页的启用状态
cat /sys/kernel/mm/transparent_hugepage/enabled
# 预期输出:always [madvise] never(方括号表示当前值)

# 设置为 always(全系统优先使用透明大页)
echo always > /sys/kernel/mm/transparent_hugepage/enabled

# 设置为 madvise(仅对显式请求的内存区域使用,推荐容器场景)
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled

使用 glibc HugeTLB 环境变量(透明大页模式)

1. 确认 THP 设为 madvise(glibc hugetlb=1 依赖 madvise 模式):
cat /sys/kernel/mm/transparent_hugepage/enabled
# 预期输出应包含 [madvise]
2. 设置 glibc hugetlb 环境变量:
export GLIBC_TUNABLES=glibc.malloc.hugetlb=1
3. 运行目标程序:
./your_application
预期效果:glibc 在首次 malloc 时会通过 madvise(MADV_HUGEPAGE) 通知内核对分配的内存区域使用透明大页,减少 TLB miss。

使用 glibc HugeTLB 环境变量(系统默认大页模式)

1. 确认系统默认大页大小:
cat /proc/meminfo | grep Hugepagesize
# 预期输出:Hugepagesize: 2048 kB
2. 预分配大页(示例:预留 520 个 2MB 大页,共计约 1040MB)。预分配的大页会从普通内存池中扣除,请您根据业务实际内存需求规划数量:
echo 520 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
3. 设置 glibc hugetlb 环境变量:
export GLIBC_TUNABLES=glibc.malloc.hugetlb=2
4. 运行目标程序(以 1GB malloc & memset 为例):
./your_application

使用 glibc HugeTLB 环境变量(自定义 1GB 大页)

1. 确认系统支持的大页规格:
ls /sys/kernel/mm/hugepages
# 预期输出:hugepages-1048576kB hugepages-2048kB
2. 预分配 1GB 大页(预分配的大页会从普通内存池中扣除,请您按业务实际需求规划数量):
echo 3 > /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
3. 设置 glibc hugetlb 环境变量(单位是字节,1GB=1073741824):
export GLIBC_TUNABLES=glibc.malloc.hugetlb=1073741824
4. 运行目标程序:
./your_application

分析 OOM Killer 日志

当系统发生 OOM 时,内核日志(/var/log/messagesjournalctl -k)会记录详细的 OOM 信息。关键日志字段含义:
日志字段
含义
invoked oom-killer
触发 OOM 的进程名和 gfp_mask
cpuset= / mems_allowed=
进程所在的 cgroup 和 NUMA 内存限制
CPU: PID: Comm:
触发进程的 PID 和命令名
Mem-Info:
系统内存使用情况(以页为单位,x86 默认 1 页=4KB)
Node X ... free: ... low:
各 NUMA 节点的 free/low 内存,free < low 是 OOM 的直接原因
Killed process ...
被 OOM Killer 选中的进程及其 oom_score
# 查看最近的 OOM 日志
journalctl -k | grep -i "oom\\|killed process" | tail -20

# 调整进程的 OOM 评分(降低核心业务被杀概率)
echo -500 > /proc/<pid>/oom_score_adj

# 完全禁止某进程被 OOM Killer 杀掉
echo -1000 > /proc/<pid>/oom_score_adj
注意:
oom_score_adj 设为 -1000 后,系统整体内存耗尽时 OOM Killer 会转而杀掉其他进程。建议优先使用 cgroup memory.max 限制单进程内存。

配置 cgroup v2 内存限制

# 创建一个内存受限的 cgroup
mkdir /sys/fs/cgroup/myapp

# 设置硬限制(1GB)
echo 1073741824 > /sys/fs/cgroup/myapp/memory.max

# 设置软限制(512MB,超过后触发回收但不杀进程)
echo 536870912 > /sys/fs/cgroup/myapp/memory.high

# 将目标进程加入 cgroup
echo $PID > /sys/fs/cgroup/myapp/cgroup.procs

# 查看 cgroup 内存统计
cat /sys/fs/cgroup/myapp/memory.stat
预期效果:当 myapp cgroup 内存使用超过 memory.high(512MB)时,内核开始回收该 cgroup 的内存;超过 memory.max(1GB)时触发 OOM Killer 杀掉该 cgroup 中内存占用最大的进程,不会影响其他 cgroup 中的业务。

常见问题

Q1:设置透明大页为 always 后,数据库延迟抖动加剧

原因:THP 设为 always 时,内核会在后台进行内存碎片整理(khugepaged),可能导致偶发延迟。
解决:对延迟敏感型业务,改用 madvise 模式,由应用显式请求大页:
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled

Q2:HugeTLB 预留大页后普通内存不足

原因:预分配的大页从普通内存池中扣除,预留过多会导致普通应用可用内存减少。
解决:根据业务实际需求调整 nr_hugepages,通过 cat /proc/meminfo | grep HugePages 监控大页使用率,空闲大页可回收:
# 查看大页使用情况
grep -i huge /proc/meminfo
# 减少预留大页数量
echo 0 > /proc/sys/vm/nr_hugepages

Q3:glibc hugetlb 环境变量设置后无效果

原因:glibc 版本低于 2.35 时会静默忽略该变量,或未预分配足够的大页。
解决:确认 glibc 版本是否 > 2.35。
# 1. 确认 glibc 版本
ldd --version
# 2. 确认大页已预分配
grep HugePages_Total /proc/meminfo
# 3. 若大页不足,先分配(注意:预分配的大页会从普通内存池中扣除)
echo <页数> > /proc/sys/vm/nr_hugepages

Q4:OOM Killer 杀掉了核心业务进程

原因:OOM Killer 按 oom_score 选进程,不考虑业务重要性。核心业务内存占用高时容易被选中。
解决:调整 oom_score 的取值,或者使用 cgroup 隔离。
# 方式1:降低核心进程的 oom_score(-1000 表示完全禁止被杀;注意系统整体内存耗尽时 OOM Killer 会转而杀其他进程)
echo -1000 > /proc/<PID>/oom_score_adj

# 方式2:用 cgroup 隔离(推荐,超过 memory.max 时仅杀该 cgroup 内进程,不影响其他业务)
echo <限制字节> > /sys/fs/cgroup/<cgroup_name>/memory.max

Q5:cgroup memory.high 设置后应用性能下降

原因:memory.high 设置过低,导致内核频繁回收内存,影响应用性能。
解决:建议 memory.high 设为 memory.max 的 80%~90%,避免频繁回收。