Transparent Huge Pages

1.背景:为什么需要大页

  现代操作系统通过虚拟内存管理进程地址空间。应用看到的是连续的虚拟地址,内核和 MMU 负责把虚拟地址映射到物理内存页。

  常规内存页通常是 4KB。假设一个进程使用 8GB 内存:

Plain Text
8GB / 4KB = 2,097,152 个页

  这意味着系统需要维护大量页表项,CPU 在访问内存时也需要频繁做虚拟地址到物理地址的转换。

  为减少转换开销,CPU 使用 TLB(Translation Lookaside Buffer)缓存近期的地址映射。但 TLB 容量有限,页越小,同样内存规模需要的页表项越多,TLB miss 的概率越高。

  大页的目标就是:用更大的页映射同样的内存区域,减少页表项数量,降低 TLB miss。

  常见页大小:

类型 常见大小 特点
普通页 4KB 默认内存页,粒度细,碎片压力小
Huge Page 2MB 常见大页,减少页表和 TLB 压力
Huge Page 1GB 更大粒度,通常用于特殊高性能场景

  示意:

image

 

2.大页的两种使用方式

  Linux 中常见的大页机制有两类:

  1. 显式 HugeTLB
  2. Transparent Huge Pages,简称 THP

2.1 显式 HugeTLB

  显式 HugeTLB 需要提前在系统中预留大页,应用也需要明确申请使用。

  特点:

  • 行为确定
  • 需要预留内存
  • 使用成本较高
  • 不会被普通内存分配自动占用
  • 适合数据库、DPDK、HPC、低延迟系统等场景

  典型查看方式:

cat /proc/meminfo | grep -i huge

  典型 JVM 参数:

-XX:+UseLargePages

 

2.2 Transparent Huge Pages

  THP 的目标是让应用无需显式改造,也能自动使用大页。

  它主要针对匿名内存,比如进程 heap、mmap 匿名映射等。内核会尝试把连续的 4KB 页合并成 2MB 大页,或者在分配内存时直接分配 2MB 大页。

  特点:

  • 对应用透明
  • 不需要应用显式申请 HugeTLB
  • 降低使用门槛
  • 自动决策不一定准确
  • 可能引入内存规整、延迟抖动和内存碎片问题

  THP 的核心问题不在“大页”,而在“自动”。

 

3.THP 的工作模式

  THP 常见配置位于:

/sys/kernel/mm/transparent_hugepage/enabled

  查看:

cat /sys/kernel/mm/transparent_hugepage/enabled

  常见输出:

[always] madvise never

  含义:

模式 含义
always 内核尽量自动为匿名内存使用 THP
madvise 只有应用通过 madvise 声明适合大页的区域才使用 THP
never 禁用 THP

  Linux Kernel 官方文档明确提供了 always、madvise、never 三种控制方式,并说明可以把 THP 限制在 MADV_HUGEPAGE 标记的内存区域,以避免无差别启用带来的额外内存消耗风险。参考:Transparent Hugepage Support - Linux Kernel Documentation

  Alibaba Cloud Linux 官方文档也对 madvise 做了相同定义:只有通过 madvise() 系统调用并设置 MADV_HUGEPAGE 标记的内存区域才开启 THP。这个定义可以作为生产中推荐 madvise 而不是 always 的补充依据。参考:Alibaba Cloud Linux 系统中与透明大页 THP 相关的性能调优方法

  还有一个常见配置:

/sys/kernel/mm/transparent_hugepage/defrag

  它影响 THP 分配失败时是否进行内存规整。

 

4.THP 的核心机制

  THP 通常通过两条路径生效:

  1. 分配时直接使用大页
  2. 后台线程把普通页合并为大页

 

4.1 分配时直接使用大页

当进程申请内存并触发缺页异常时,内核可能尝试直接分配一个 2MB 大页。

如果系统有连续的 2MB 物理内存,分配较顺利。

如果没有连续 2MB 物理内存,内核可能:

    • 回退到 4KB 普通页
    • 触发内存规整
    • 根据 defrag 策略产生同步等待

  示意:

image

  

4.2 khugepaged 后台合并

  Linux 内核中有一个后台线程 khugepaged,会扫描符合条件的内存区域,把多个连续的 4KB 页合并成 2MB THP。

  这个过程称为 collapse。

  示意:

image 

  2MB / 4KB = 512,因此一个 2MB THP 对应 512 个普通页。

 

5.THP 的收益

  THP 主要收益来自地址转换成本降低。

5.1 减少页表项

  大页可以显著减少页表项数量。页表更小,页表遍历成本更低。

5.2 降低 TLB miss

  TLB 缓存的是虚拟页到物理页的映射。页越大,一个 TLB entry 覆盖的内存范围越大。

1 个 4KB 页 TLB entry 覆盖 4KB
1 个 2MB 页 TLB entry 覆盖 2MB

  这对大内存、顺序访问、热点区域稳定的程序更有价值。

5.3 减少 page fault 次数

  对于大块连续内存分配,使用 THP 后,可能减少部分缺页处理次数。

 

6.THP 的风险

  THP 的风险主要来自自动分配、自动合并和大页对连续物理内存的要求。

6.1 内存碎片

  THP 需要连续的 2MB 物理内存。系统运行一段时间后,物理内存可能被不同生命周期的页切碎。

  示意:

理想状态:
[---------------- 2MB 连续空闲内存 ----------------]

碎片状态:
[空闲][已用][空闲][已用][空闲][已用][空闲][已用]

  碎片状态下,即使总空闲内存足够,也可能没有连续 2MB 空间。

6.2 内存规整带来的延迟抖动

  为了获得连续 2MB 空间,内核可能触发 compaction,把可移动页搬到一起,整理出连续空闲空间。

  这会消耗 CPU,并可能造成请求延迟抖动。

  对低延迟服务来说,P99/P999 受影响的风险比平均吞吐更重要。

6.3 内存浪费

  如果应用只使用了大页中的一小部分,也可能导致实际内存占用放大。

  例如,一个 2MB THP 中只有少量数据是活跃的,但整个大页仍被视为占用。

  Alibaba Cloud Linux 文档把这类问题称为 Memory bloating。典型例子是应用实际只需要少量 4KB 小页,但内核分配了一个 2MB THP,剩余大量全零子页也会被计入 RSS,从而可能导致容器或 cgroup 视角下内存占用膨胀,严重时触发 OOM。参考:THP reclaim 功能

6.4 自动决策不理解业务语义

  内核只能根据内存区域、访问模式和系统状态做启发式判断。它不知道这块内存是不是长期热点,也不知道业务是否更关注吞吐还是尾延迟。

  这也是生产环境中经常不建议使用 always 的核心原因。

6.5 THP reclaim

  在 Alibaba Cloud Linux 中,THP reclaim 是一个用于缓解 THP memory bloating 的机制。它可以在 memcg 粒度把透明大页拆分为小页,并回收其中的全零页面,从而降低 RSS 膨胀引发 OOM 的风险。

  需要注意,THP reclaim 的代价是会把 2MB THP 拆成 4KB 小页,因此可能带来一定内存访问性能回退。它更像是对 THP 副作用的缓解机制,而不是证明 THP 可以无风险全局开启。

  相关接口包括:

/sys/kernel/mm/transparent_hugepage/reclaim
memory.thp_reclaim
memory.thp_reclaim_stat
memory.thp_reclaim_ctrl

  其中 memory.thp_reclaim_stat 可观察:

queue_length
split_hugepage
reclaim_subpage

 

7.THP 如何影响 JVM

  JVM 是 THP 讨论中非常典型的场景。原因是 Java 应用通常有较大的 heap,且 heap 是匿名内存,容易成为 THP 作用对象。

7.1 JVM 内存结构

  JVM 进程内存大致包括:

  • Java Heap
  • Metaspace
  • Code Cache
  • Thread Stack
  • Direct Buffer
  • GC 相关结构
  • JVM Native 内存

  示意:

image 

  THP 最常影响的是 Java Heap,因为 heap 通常较大、连续、匿名映射。

  观察 JVM 内存时,需要区分 JVM 内部视角和容器外部视角。

  例如,JVM 内部通过 heap、non-heap、NMT 或 GC 日志看到的内存占用可能是 1GB,但从 Pod 或 cgroup 外部看到的 RSS / working set 可能是 1.5GB。这不一定表示 JVM 统计错误,而是统计口径不同。

  常见差异来源:

  • JVM 内部统计通常更关注 Java Heap、Metaspace、Code Cache、Direct Buffer、线程等 JVM 可归因内存。
  • Pod / cgroup 视角统计的是整个进程组对内核资源的实际占用,通常包括 Java Heap、JVM native 内存、线程栈、JNI/native library、allocator 缓存、页表、mmap 区域、文件映射、page cache 相关计量等。
  • JVM allocator 或 native library 释放内存后,内存可能仍被进程 allocator 缓存,未立即归还给 OS。
  • THP 使用 2MB 粒度的大页后,外部 RSS 或 cgroup 口径可能更容易看到按大页粒度体现的占用变化。

  因此,排查 JVM 内存问题时不能只看 JVM 内部指标,也不能只看 Pod RSS。更合理的方式是把两类口径放在一起看:

JVM 内部视角:heap / non-heap / NMT / GC log
Pod 外部视角:container_memory_working_set_bytes / RSS / cgroup memory.current

  如果二者差距较大,优先进一步拆分 native memory、Direct Buffer、线程栈、mmap、allocator 缓存和 THP 使用情况。

 

7.2 JVM 使用 THP 的方式

  JVM 中和大页相关的参数常见有两类:

-XX:+UseTransparentHugePages

  以及:

-XX:+UseLargePages

  二者含义不同:

JVM 参数 对应机制 特点
-XX:+UseTransparentHugePages THP 依赖内核 THP,通常配合 madvise
-XX:+UseLargePages 显式 HugeTLB 需要系统预留 hugepages,确定性更强

  推荐理解:

UseTransparentHugePages = JVM 告诉内核:这些区域可以考虑用 THP
UseLargePages = JVM 明确申请系统预留的大页

 

7.3 为什么 JVM 自己的 THP 开关通常优于 kernel always

  如果内核 THP 是 always,内核会尽量对所有符合条件的匿名内存自动使用 THP。

  如果内核 THP 是 madvise,再由 JVM 使用:

-XX:+UseTransparentHugePages

  那么 THP 的影响范围会收敛到 JVM 主动声明的内存区域。

  示意:

image

  因此,JVM 自己的 THP 开关通常比 kernel always 更可控。

  但它并不消除 THP 的全部风险。只要最终使用的是 THP,就仍然可能受 compaction、collapse、split、NUMA、内存碎片等因素影响。

 

7.4 GC 的影响

  THP 对 GC 的影响不是单向的。

  可能收益:

  • 大 heap 场景下减少 TLB miss
  • 降低页表开销
  • 对吞吐型 GC 可能有正收益

  可能风险:

  • GC 期间访问大量内存时,大页 split 或 page fault 可能带来抖动
  • 内存规整可能影响应用线程或 GC 线程延迟
  • 对低延迟 GC,尾延迟风险需要重点验证

  不同 GC 的敏感点:

GC 关注点
Parallel GC 吞吐优先,可能更容易从大页中受益
G1 GC 大 heap 常见,需要关注 pause time 和 remembered set 行为
ZGC/Shenandoah 低延迟优先,更需要关注尾延迟和内存映射行为

结论不是“JVM 应该开 THP”或“JVM 不应该开 THP”,而是:

JVM 使用 THP 必须结合 GCheap 大小、访问模式、延迟目标和压测数据判断。

 

8.K8s 节点上的 THP 策略

对于通用 Kubernetes 节点,通常不建议使用 THP always。

原因:

  • Pod 生命周期复杂
  • 多进程、多语言、多内存模型混部
  • 内存申请释放频繁
  • 节点资源共享
  • 自动 THP 决策影响面过大
  • 尾延迟问题排查困难

更保守的生产策略:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

或者:

echo madvise > /sys/kernel/mm/transparent_hugepage/enabled

推荐 madvise 的依据是:Linux Kernel 文档说明 madvise 可以把 THP 使用范围限制在应用显式声明的区域;madvise(2) 手册也说明 MADV_HUGEPAGE 适用于开发者明确知道访问模式、且不会增加内存占用风险的内存区域。参考:madvise(2) - Linux manual page

Alibaba Cloud Linux 的 THP 调优文档也指出,THP 对系统性能的影响不能一概而论,需要根据业务、系统和应用实际情况调整;对于数据库、大量延迟敏感应用、大量短生命周期内存分配等场景,如果稳定性比性能更重要,建议关闭 THP。参考:Alibaba Cloud Linux 系统中与透明大页 THP 相关的性能调优方法

如果某些 workload 确实需要大页,优先考虑:

  1. 独立 node pool
  2. node label / taint 隔离
  3. K8s HugePages 显式资源
  4. JVM 或应用显式参数
  5. 压测验证

K8s 显式 HugePages 示例:

YAML
resources:
  requests:
    hugepages-2Mi: 1Gi
    memory: 2Gi
  limits:
    hugepages-2Mi: 1Gi
    memory: 2Gi

Kubernetes 官方文档支持把 HugePages 作为可调度资源显式声明,并要求节点预先分配 huge pages;这更符合 K8s 的资源隔离和可调度模型。参考:Manage HugePages - Kubernetes Documentation

 

9.如何观察 THP

9.1 查看系统配置

cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/defrag

9.2 查看系统 THP 使用情况

grep -i huge /proc/meminfo

重点关注:

Plain Text
AnonHugePages
ShmemHugePages
FileHugePages
HugePages_Total
HugePages_Free
Hugepagesize

其中 AnonHugePages 通常可以反映匿名内存中 THP 使用情况。

9.3 查看进程级 THP

cat /proc/<pid>/smaps | grep -i AnonHugePages

或者汇总:

grep -i AnonHugePages /proc/<pid>/smaps | awk '{sum += $2} END {print sum " kB"}'

9.4 查看 khugepaged 指标

不同内核版本路径可能略有差异,常见位置:

/sys/kernel/mm/transparent_hugepage/khugepaged/

可查看:

ls /sys/kernel/mm/transparent_hugepage/khugepaged/

常见指标包括:

  • pages_collapsed
  • full_scans
  • scan_sleep_millisecs
  • alloc_sleep_millisecs

 

10.压测建议

THP 是否有收益,不能只看平均吞吐。

建议至少对比三组:

  1. THP never
  2. THP madvise
  3. THP madvise + JVM -XX:+UseTransparentHugePages

如果是专用节点,也可以加一组:

  1. THP always

关键指标:

指标 说明
平均吞吐 是否真的提升
P95/P99/P999 延迟 是否引入尾延迟抖动
CPU 使用率 是否减少地址转换开销,或增加 compaction 开销
GC pause JVM 场景必须关注
page fault 是否出现异常波动
AnonHugePages THP 是否真的生效
compact_stall 是否发生内存规整等待
Broker 网络和磁盘指标 避免误把 IO 瓶颈归因到 THP

Linux VM 统计中可以关注:

cat /proc/vmstat | grep -E 'compact|thp'

常见指标:

thp_fault_alloc
thp_fault_fallback
thp_collapse_alloc
thp_collapse_alloc_failed
compact_stall
compact_fail
compact_success

 

11.推荐结论

11.1 通用结论

THP 的价值在于降低页表和 TLB 成本,适合大内存、访问模式稳定、吞吐敏感的场景。

THP 的风险在于它是自动机制,内核不能准确理解业务内存语义,可能引入内存规整、碎片、内存浪费和尾延迟抖动。

 

11.2 生产建议

场景 建议
普通 K8s 节点 never 或 madvise
混部微服务节点 避免 always
JVM 普通业务服务 默认不开,压测后决定
数据库 / DPDK / HPC 优先考虑显式 HugeTLB
极致低延迟服务 谨慎使用 THP,重点验证 P99/P999

Red Hat 官方性能调优文档也指出,THP 自动化了 huge page 的创建、管理和使用,但并不适合所有 workload,并明确提到 THP 不推荐用于数据库 workload。参考:Huge Pages and Transparent Huge Pages - Red Hat Documentation

Alibaba Cloud Linux 官方文档进一步补充了几个生产风险点:defrag=always 可能在内存紧张时触发同步直接回收和直接整理;khugepaged 合并大页时会在内存路径中加锁;如果在错误时间触发扫描和转换,可能影响内存敏感型应用。该文档也建议在部分场景下评估 defer+madvise,在性能和平稳性之间折中。参考:Alibaba Cloud Linux 系统中与透明大页 THP 相关的性能调优方法

 

11.3 一句话总结

大页本身不是问题,问题是自动大页决策是否真的理解业务场景。对于通用 K8s 节点,THP always通常不值得;对于数据库、DPDKHPC 等专用 workload,可以通过隔离节点、显式声明和压测验证来使用大页能力

 

12.为什么小白集群没有此问题

   Alibaba Cloud Linux 3(基于Anolis OS)与 Alibaba Cloud Linux 2(基于CentOS 7)在透明大页(THP, Transparent Huge Pages)的默认行为上存在以下主要区别:

1.默认启用状态

    • Alibaba Cloud Linux 2:默认启用 THP([always] madvise never),适用于通用场景,但对延迟敏感型应用(如数据库)可能产生性能影响。
    • Alibaba Cloud Linux 3:默认改为 madvise 模式(always [madvise] never),仅对明确使用 MADV_HUGEPAGE 的进程启用 THP,减少对未优化应用的干扰。

2.配置路径一致性

    • 两者均通过 /sys/kernel/mm/transparent_hugepage/enabled 控制 THP 状态,命令操作方式相同。

3.推荐操作

    • 若运行 Redis、MongoDB 等数据库,建议在两系统中均显式关闭 THP:
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
    • 永久生效需添加到 /etc/rc.local 或创建 systemd 服务。

可通过以下命令检查当前 THP 状态:

cat /sys/kernel/mm/transparent_hugepage/enabled

 

13.官方参考资料

  1. Transparent Hugepage Support - Linux Kernel Documentation
  2. madvise(2) - Linux manual page
  3. Manage HugePages - Kubernetes Documentation
  4. Huge Pages and Transparent Huge Pages - Red Hat Documentation
  5. Alibaba Cloud Linux 系统中与透明大页 THP 相关的性能调优方法
  6. THP reclaim 功能 - Alibaba Cloud Linux
posted @ 2026-07-23 17:46  小家电维修  阅读(22)  评论(0)    收藏  举报