1. linux 内存管理单元介绍
Linux 系统管理内存的最小单元为页,默认页大小为 4KB,可通过 getconf PAGESIZE 查看。
程序访问的是虚拟内存,虚拟地址与物理地址的转换关系记录在页表(Page Table)中。
当 CPU 访问虚拟地址时,会优先查询 TLB(地址转换旁路缓冲存储器),TLB 缓存了常用的虚拟地址→物理地址映射。
如果 TLB 中没有找到(TLB Miss),CPU 会去内存中查询页表,并把新的映射关系缓存到 TLB。
但 TLB 容量很小,能缓存的页表映射条目有限。如果申请内存较大,对应的内存页数量就会非常多,导致 TLB 频繁失效。
这时 CPU 就需要频繁访问内存中的页表查找地址映射,CPU 负载随之升高。
为了降低CPU的负载,就可以通过巨页来增大内存页的大小,通过TLB就能更快的访问到物理内存。
查看系统默认的页大小:

2. 巨页介绍
linux 巨页技术有两个:
-
Huge Pages(静态巨页):
静态巨页,就是系统启动时就预先分配好、数量固定、位置固定、不会被内核动的大页内存。
通过大幅减少页表数量、提高 TLB 命中率,显著降低 CPU 地址翻译负担。
-
Transparent Huge Pages (透明巨页)
THP 透明巨页是内核自动把 4KB 页合并成 2MB 巨页的机制,合并时会卡顿,对应用透明。
优点是自动优化、无需配置;
缺点是会造成内存紧缩、CPU 毛刺、IO 波动。
查看是否开启巨页:
查看是否开启透明巨页:
cat /sys/kernel/mm/transparent_hugepage/enabled
csharp
[always] madvise never # 当前状态是开启的
- always:完全开启状态
- madvise:只有程序显式要求时才用巨页
- never: 完全关闭透明巨页
查看是否开启静态巨页:
grep -i huge /proc/meminfo | grep HugePages_Total
csharp
HugePages_Total: 0
0 代表未开启,大于0等于分配了多少巨页
开启两种巨页的方式:
开启静态巨页:
# 临时开启:
echo 256 > /proc/sys/vm/nr_hugepages # 提前分配256个2MB的巨页
# 永久开启:
echo "vm.nr_hugepages=256" >>/etc/sysctl.conf
sysctl -p # 立即生效
csharp
[root@localhost ~]# cat /proc/meminfo | grep -E 'MemFree|Hugepagesize|HugePages_Total'
MemFree: 1481000 kB # 未开启巨页前当前剩余内存量
HugePages_Total: 0 # 未开启巨页前巨页数量
Hugepagesize: 2048 kB # 默认巨页大小
[root@localhost ~]# echo 256 > /proc/sys/vm/nr_hugepages # 提前分配256个2MB的巨页
[root@localhost ~]# cat /proc/meminfo | grep -E 'MemFree|Hugepagesize|HugePages_Total'
MemFree: 955456 kB # 开启巨页后的内存剩余量已经减少了
HugePages_Total: 256 # 当前巨页数量
Hugepagesize: 2048 kB
开启透明巨页:
# 临时开启
echo always > /sys/kernel/mm/transparent_hugepage/enabled # 开启透明巨页
echo always > /sys/kernel/mm/transparent_hugepage/defrag # 开启透明巨页的内存碎片整理
# 永久开启
cat > /etc/systemd/system/enable-transparent-huge-pages.service <<EOF
[Unit]
Description=Enable Transparent Hugepages (THP)
DefaultDependencies=no
After=sysinit.target local-fs.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo always | tee /sys/kernel/mm/transparent_hugepage/enabled > /dev/null && echo always | tee /sys/kernel/mm/transparent_hugepage/defrag > /dev/null'
[Install]
WantedBy=basic.target
EOF
systemctl daemon-reload
# 或者兼容老的系统
cat >> /etc/rc.local <<EOF
echo always > /sys/kernel/mm/transparent_hugepage/enabled
echo always > /sys/kernel/mm/transparent_hugepage/defrag
EOF
chmod +x /etc/rc.local

可以看出系统初始被被内核自动合并为 2MB 巨页的内存为4096 kB,后面执行stress-ng 压测后,被自动合并的内存页明显上升了。
关闭透明巨页:
# 临时关闭透明巨页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 永久关闭,如果之前有设置开启的service文件,记得disable掉
cat > /etc/systemd/system/disable-transparent-huge-pages.service <<EOF
[Unit]
Description=Disable Transparent Hugepages (THP)
DefaultDependencies=no
After=sysinit.target local-fs.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled && echo never > /sys/kernel/mm/transparent_hugepage/defrag'
[Install]
WantedBy=basic.target
EOF
systemctl daemon-reload
关闭静态巨页:
# 临时关闭:
echo 0 > /proc/sys/vm/nr_hugepages # 提前分配256个2MB的巨页
# 永久关闭:
echo "vm.nr_hugepages=0" >>/etc/sysctl.conf
sysctl -p # 立即生效
3. 透明巨页和静态巨页的使用场景
-
透明巨页
内核自动合并内存,在合并的时候会有卡顿,不适合高IO场景,对延时敏感,不能接受突然卡顿几毫秒~几十毫秒。
适合对延时不敏感的业务比如:文件服务器,云盘 / 对象存储类服务,日志收集、备份服务器。
-
静态巨页 HugePages
内核一次性分配,提前占用内存,不适合小内存服务器,一次性分配内存很小的程序。
适合申请内存比较多的业务且不经常回收: 虚拟机、redis、mysql。
参考文章:
https://www.jianshu.com/p/391f42f8fb0d
https://mp.weixin.qq.com/s/1CLV53M-1-pomC5Zsnu_dw
https://www.mongodb.com/zh-cn/docs/manual/tutorial/disable-transparent-huge-pages/
https://blog.csdn.net/TiDB_PingCAP/article/details/109227464