Linux 内存调优之如何限制进程、系统级别内存资源

限制内存使用量

今天和小伙伴分享一些Linux 内存限制相关知识,主要涉及如何配置以及什么情况下需要配置,我们知道内存属于不可压缩资源,当没有那么多的物理内存可以映射,进程都无法启动,所以为了公平,亦或是考虑部分进程 Qos 级别,一般情况下会对进程进行内存限制,即保证机器上的多个进程不会因为业务对基础资源的弹性要求,相互影响,比如类似FTP进程的内存泄露问题影响到核心业务服务触发 OOM ,甚至直接被OOM killer 掉。

简单介绍,关于内存资源限制在 Linux 中,一般按照限制手段来划分的话,分为

内核参数(包括启动配置)限制: 临时修改交换分区频率sysctl -w vm.swappiness=10, 启动引导配置大页参数grubby --update-kernel=ALL --args="hugepagesz=1G hugepages=10"。Cgroup(包括systemd)限制: 通过 Cgroup 的 memory 子系统限制 /sys/fs/cgroup/memory/myapp/memory.limit_in_bytes,MemoryMax=1G。ulimit 限制: ulimit -v 2097152。

三种方式,按照限制类型划分,一般分为:

系统内存限制: 比如修改内核参数sysctl -w vm.overcommit_memory=2  禁止过度分配虚拟内存。进程内存限制: Systemd 单元限制进程物理内存不超过 1G MemoryMax=1G。用户会话内存限制: echo "john hard as 2097152" >> /etc/security/limits.conf 限制用户 john 的进程最大虚拟内存(地址空间)为 2 GB。

如果按照具体的内存种类划分,可以分为:

物理、虚拟内存限制: 硬限制物理内存大小 memory.limit_in_bytes, 会话级别虚拟内存限制 ulimit -v。数据段,数据栈限制: ulimit -d 262144 设置数据段的最大值。数据缓存区: 释放内存缓存区设置 vm.drop_caches,网络IO 相关缓存区配置 net.ipv4.tcp_rmem。大页,脏页相关内存页限制:大页大小 vm.nr_hugepages。

Cgroup

Cgroup(Control Groups)最早由 google 开发,后来内置到了 Linux 内核中,是Linux kernel(Linux内核)的一项功能,目前是很多虚拟化容器技术的底层核心技术。

在一个系统中运行的层级制进程组,Cgroup 可对其进行资源分配(如CPU时间、系统内存、网络带宽或者这些资源的组合)。

通过使用cgroup ,系统管理员在分配、排序、拒绝、管理和监控系统资源等方面,可以进行精细化控制。硬件资源可以在应用程序和用户间智能分配,从而增加整体效率。

Cgroup可对进程进行层级式分组并标记,并对其可用资源进行限制。通过将cgroup层级系统与systemd单位树捆绑, Linux 可以把资源管理设置从进程级别移至应用程序级别。

可以使用systemctl指令,或者通过修改systemd单位文件来管理系统资源。

为了控制重要的内核资源,systemd 会自动挂载/sys/fs/cgroup 目录实现 cgroup 分层架构,Linux 内核的资源管理器,也叫 CGroup 子系统,代表某一种单一资源(如 CPU 时间或内存等。

Linux 内核提供了一系列资源管理器,由 systemd 自动挂载。如果需要查看已经挂载的资源管理器列表,可以参考/proc/cgroups。

内存子系统位于其中:

memory: 对 cgroup 中的任务使用内存量进行限制,并且自动生成任务占用内存资源的报告。

在安装了 kernel-doc 软件包后,可以在/usr/share/doc/kernel-doc-<version>/Documentation/cgroup 目录下找相关管理器的说明文档,从而配置合适的资源限制。

复制
┌──[root@liruilongs.github.io]-[/usr/share/doc/kernel-doc-4.18.0/Documentation/cgroup-v1] └─$ls 00-INDEX cgroups.txt cpusets.txt freezer-subsystem.txt memcg_test.txt net_cls.txt pids.txt blkio-controller.txt cpuacct.txt devices.txt hugetlb.txt memory.txt net_prio.txt rdma.txt ┌──[root@liruilongs.github.io]-[/usr/share/doc/kernel-doc-4.18.0/Documentation/cgroup-v1] └─$ll total 184 。。。。。。。。。。。。。。。。 -r--r--r--. 1 root root 8480 Mar 27 2020 memcg_test.txt -r--r--r--. 1 root root 37743 Mar 27 2020 memory.txt ................1.2.3.4.5.6.7.8.9.10.11.

简单的信息可以通过索引文件的了解。

复制
┌──[root@liruilongs.github.io]-[/usr/share/doc/kernel-doc-4.18.0/Documentation/cgroup-v1] └─$cat 00-INDEX 。。。。。。。 memcg_test.txt - Memory Resource Controller; implementation details. memory.txt - Memory Resource Controller; design, accounting, interface, testing. 。。。。。。。。。。。。。。。。。。。。。 ┌──[root@liruilongs.github.io]-[/usr/share/doc/kernel-doc-4.18.0/Documentation/cgroup-v1] └─$1.2.3.4.5.6.7.8.9.10.

Cgroup 限制内存资源常见的有两种,一种是 通过 systemd,一种是直接通过 cgroup 文件系统进行配置,这里我们先介绍通过 systemd 的方式配置,因为这种方式比较简单,而且可以做到进程级别的配置,而通过 cgroup 的方式,可以做到系统级别的配置,前提是当前Linux 机器使用 systemd,并且所有启动进程纳管到了 cgroup 子树,那么可以通过限制顶层树的资源限制,来实现整个系统的资源限制。

在 systemd 的 drop-in 文件文件[Service]段里面定义 MemoryLimit 值就可以限制你的程序所使用的内存,单位可以是K,M,G或T。

这里看一个以临时服务的方式运行 /bin/bash命令的Demo ,对 内存和CPU 进行限制, 并将其标准输入、标准输出、标准错误连接到当前的 TTY 设备上:

复制
┌──[root@liruilongs.github.io]-[~] └─$ systemd-run -p MemoryLimit=5M -p CPUShares=100 --unit=bash-limit --slice=bash-test -t /bin/bash Running as unit bash-limit.service. Press ^] three times within 1s to disconnect TTY.1.2.3.4.

在生成的 bash Service 中我们可以运行交互命令,查看当前 Service 的单元文件,MemoryLimit=5242880 ,即限制内存使用量 5M。

复制
┌──[root@liruilongs.github.io]-[/] └─$ systemctl cat bash-limit.service # /run/systemd/system/bash-limit.service # Transient stub # /run/systemd/system/bash-limit.service.d/50-CPUShares.conf [Service] CPUShares=100 # /run/systemd/system/bash-limit.service.d/50-Description.conf [Unit] Description=/bin/bash # /run/systemd/system/bash-limit.service.d/50-Environment.conf [Service] Environment="TERM=xterm-256color" # /run/systemd/system/bash-limit.service.d/50-ExecStart.conf [Service] ExecStart= ExecStart=@/bin/bash "/bin/bash" # /run/systemd/system/bash-limit.service.d/50-MemoryLimit.conf [Service] MemoryLimit=5242880 # /run/systemd/system/bash-limit.service.d/50-Slice.conf 。。。。。。。。。。。。。。。。。。。。。。。。1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.

通过 systemctl status bash-limit.service 我们可以看到cgroup的相关信息。

复制
┌──[root@liruilongs.github.io]-[/] └─$ systemctl status bash-limit.service ● bash-limit.service - /bin/bash Loaded: loaded (/run/systemd/system/bash-limit.service; static; vendor preset: disabled) Drop-In: /run/systemd/system/bash-limit.service.d └─50-CPUShares.conf, 50-Description.conf, 50-Environment.conf, 50-ExecStart.conf, 50-MemoryLimit.conf, 50-Slice.conf, 50-StandardError.conf, 50-StandardInput.conf, 50-StandardOutput.conf, 50-TTYPath.conf Active: active (running) since 六 2022-10-29 13:40:19 CST; 31s ago Main PID: 136529 (bash) Memory: 1.7M (limit: 5.0M) CGroup: /bash.slice/bash-test.slice/bash-limit.service ├─136529 /bin/bash └─136607 systemctl status bash-limit.service 10月 29 13:40:19 liruilongs.github.io systemd[1]: Started /bin/bash. ┌──[root@liruilongs.github.io]-[/] └─$ bash ┌──[root@liruilongs.github.io]-[/] └─$ bash1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.

bash-limit.service 这个 service  的上级子树为 bash.slice 这个分组。

复制
Memory: 1.7M (limit: 5.0M) CGroup: /bash.slice/bash-test.slice/bash-limit.service ├─136529 /bin/bash └─136607 systemctl status bash-limit.service1.2.3.4.

当然上面的配置方式实际上是基于 Cgroup 来实现的,Cgroup V1 版本和 V2 版本有些区别,当前机器环境的问题,我们只看一下 V1 的版本,MemoryLimit 参数可以控制Cgroup 内存控制器的 memory.limit_in_bytes Cgroup参数。

对于运行中的 service 可以直接通过set-property 的方式来修改。

复制
# 如需使用命令列来限定 httpd.service 的 CPU 和内存占用量,请输入: systemctl set-property httpd.service CPUShares=600 MemoryLimit=500M1.2.

下面为 system.slice 这个 Cgroup 分组下面 tuned Cgroup 内存相关资源限制,可以看到默认的情况下没有限制(memory.limit_in_bytes ),使用的最大值,这里的内存限制是物理内存,不是虚拟内存。

tuned 小伙伴们应该不陌生,一个开源的系统性能优化的服务,用于一些内核参数限制。

复制
┌──[root@liruilongs.github.io]-[/sys/fs/cgroup/memory/system.slice] └─$cat tuned.service/memory. memory.events memory.kmem.tcp.failcnt memory.memsw.failcnt memory.qos_level memory.events.local memory.kmem.tcp.limit_in_bytes memory.memsw.limit_in_bytes memory.soft_limit_in_bytes memory.failcnt memory.kmem.tcp.max_usage_in_bytes memory.memsw.max_usage_in_bytes memory.stat memory.force_empty memory.kmem.tcp.usage_in_bytes memory.memsw.usage_in_bytes memory.swappiness memory.high memory.kmem.usage_in_bytes memory.min memory.usage_in_bytes memory.kmem.failcnt memory.limit_in_bytes memory.move_charge_at_immigrate memory.use_hierarchy memory.kmem.limit_in_bytes memory.low memory.numa_stat memory.kmem.max_usage_in_bytes memory.max_usage_in_bytes memory.oom_control memory.kmem.slabinfo memory.memfs_files_info memory.pressure_level1.2.3.4.5.6.7.8.9.10.11.

默认情况下,没有限制会显示最大值。

复制
┌──[root@liruilongs.github.io]-[/sys/fs/cgroup/memory/system.slice] └─$cat tuned.service/memory.limit_in_bytes 92233720368547717121.2.3.

配置方式我们可以通过上面的方式配置,通过 service unit 文件进行限制。

限定最大可用内存为 1GB,添加 MemoryLimit 设定。

复制
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$vim tuned.service1.2.

确认配置,加载配置文件,重启。

复制
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$systemctl cat tuned.service # /usr/lib/systemd/system/tuned.service [Unit] Description=Dynamic System Tuning Daemon After=systemd-sysctl.service network.target dbus.service Requires=dbus.service polkit.service Conflicts=cpupower.service auto-cpufreq.service tlp.service power-profiles-daemon.service Documentation=man:tuned(8) man:tuned.conf(5) man:tuned-adm(8) [Service] Type=dbus MemoryLimit=1G PIDFile=/run/tuned/tuned.pid BusName=com.redhat.tuned ExecStart=/usr/sbin/tuned -l -P Restart=on-failure [Install] WantedBy=multi-user.target ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$systemctl daemon-reload ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$systemctl restart tuned.service1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.

再次查看 Cgroup 内存相关限制参数。

复制
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$cat /sys/fs/cgroup/memory/system.slice/tuned.service/memory.limit_in_bytes 1073741824 ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$1.2.3.4.5.

生产环境,更多的是通过  drop-in  的方式定义文件。

复制
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$vim tuned.service.d/50-MemoryLimit.conf ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$systemctl daemon-reload ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$systemctl restart tuned.service ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$systemctl cat tuned.service # /usr/lib/systemd/system/tuned.service [Unit] Description=Dynamic System Tuning Daemon After=systemd-sysctl.service network.target dbus.service Requires=dbus.service polkit.service Conflicts=cpupower.service auto-cpufreq.service tlp.service power-profiles-daemon.service Documentation=man:tuned(8) man:tuned.conf(5) man:tuned-adm(8) [Service] Type=dbus PIDFile=/run/tuned/tuned.pid BusName=com.redhat.tuned ExecStart=/usr/sbin/tuned -l -P Restart=on-failure [Install] WantedBy=multi-user.target # /usr/lib/systemd/system/tuned.service.d/50-MemoryLimit.conf [Service] MemoryLimit=1G ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] └─$1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.

这里我们简单看一下,其他的 Cgroup 参数限制,部分参数在 Cgroup V2 中作了调整,感兴趣小伙伴可以看看去。

核心内存限制

参数

作用

memory.limit_in_bytes

物理内存硬限制

(单位:字节),超出会触发 OOM Killer。

memory.memsw.limit_in_bytes

物理内存 + swap 总限制

(需内核启用 swapaccount=1)。

memory.soft_limit_in_bytes

内存软限制

,系统优先回收超过此值的内存,但不会强制杀死进程。

内核内存控制

参数

作用

memory.kmem.limit_in_bytes

内核内存(如 slab、dentry 缓存)的硬限制

memory.kmem.tcp.limit_in_bytes

TCP 缓冲区内存的硬限制

(如 TCP socket 发送/接收缓冲区)。

内存回收与行为控制

参数

作用

memory.force_empty

强制释放内存缓存

(写入 0 触发)。

memory.swappiness

调整内存回收策略

(值越高,系统越积极使用 swap)。

memory.oom_control

控制 OOM Killer 行为

(0 表示启用 OOM Killer,1 表示禁用)。

memory.move_charge_at_immigrate

进程迁移时是否转移内存占用

(1 表示转移)。

高级功能

参数

作用

memory.high

内存使用软限制

(v1 中较少使用,v2 中更常见)。

memory.low

内存保护阈值

,系统尽量避免回收低于此值的内存。

memory.pressure_level

内存压力事件通知

(需配合 cgroup.event_control 使用)。

对于这部分参数的配置,可以直接找到对应的 Cgroup 文件进行修改。

复制
# 限制 TCP 缓冲区为 100MB echo "100M" > memory.kmem.tcp.limit_in_bytes1.2.

ulimit

对于多用户的系统不限制资源本身可能就是一种不公平, 限制系统资源比较老的方式是使用 ulimit,由 PAM 模块在登录和会话启动时强制实施,ulimit 命令是bash 内置命令,主要限制了 shell 及其子进程可用的资源。

在/etc/pam.d/system-auth 文件中调用了 pam_limits 模块,此模块读取 /etc/security/limits.conf 和/etc/security/limits.d/,按配置文件设置资源限制。 查看模块帮助文档 man pam limits。

/etc/pam.d/system-auth 是什么?

/etc/pam.d/system-auth 是一个 PAM(Pluggable Authentication Modules)配置文件。在 Linux 系统中,PAM 提供了一种灵活的方式来配置用户认证、授权和会话管理。

该文件是一个包含 PAM 配置行的文本文件,用于定义不同的认证、授权和会话模块及其参数。PAM 模块负责处理用户登录、密码验证、权限检查等操作。

查看文件中资源限制相关的模块,有时候我们做一些基线整改,可能需要修改该文件的相关配置。

复制
┌──[root@liruilongs.github.io]-[~] └─$cat /etc/pam.d/system-auth | grep pam_limits session required pam_limits.so ┌──[root@liruilongs.github.io]-[~] └─$1.2.3.4.5.

在PAM配置中,pam_limits.so 模块被要求进行会话限制。

PAM(Pluggable Authentication Modules)是一个用于对用户进行认证的系统级框架。pam_limits.so 模块是 PAM 框架的一部分,它用于设置会话级别的资源限制,例如进程可打开的文件数、进程可使用的内存等。

ulimit 命令是用于限制用户级别资源的工具,它通常用于控制 shell 进程及其子进程的资源使用。修改 ulimit 值只会对当前 shell 会话有效,对其他用户或系统进程不会产生影响(不一定)。

通过 ulimit 是限制系统资源的一种途径,ulimit 支持 hard 和 soft 限制。

复制
#<type> can have the two values: # - "soft" for enforcing the soft limits # - "hard" for enforcing hard limits # #<item> can be one of the following: # - core - limits the core file size (KB) # - data - max data size (KB) # - fsize - maximum filesize (KB) # - memlock - max locked-in-memory address space (KB) # - nofile - max number of open file descriptors # - rss - max resident set size (KB) # - stack - max stack size (KB) # - cpu - max CPU time (MIN) # - nproc - max number of processes # - as - address space limit (KB) # - maxlogins - max number of logins for this user # - maxsyslogins - max number of logins on the system # - priority - the priority to run user process with # - locks - max number of file locks the user can hold # - sigpending - max number of pending signals # - msgqueue - max memory used by POSIX message queues (bytes) # - nice - max nice priority allowed to raise to values: [-20, 19] # - rtprio - max realtime priority1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.

普通用户可以设置自己的软限制,但不能高于硬限制。可以使用 ulimit -a 查看资源限制列表。

软限制 (soft maxlogins):软限制是一个警告阈值,当达到或超过该限制时,系统会发出警告信息,但不会阻止用户登录。硬限制 (hard maxlogins):硬限制是一个严格的限制,当达到或超过该限制时,系统将阻止用户登录。

复制
┌──[root@liruilongs.github.io]-[~] └─$ulimit -Hn #限制数 262144 ┌──[root@liruilongs.github.io]-[~] └─$ulimit -Sn #限制数 1024 ┌──[root@liruilongs.github.io]-[~] └─$1.2.3.4.5.6.7.8.

当指定限制数时限制,不指定时输出当前设置。

通过配置文件的方式对登录次数进行限制,配置 kiosk 组 在多个终端中只能同时登录 2 次。

复制
┌──[root@liruilongs.github.io]-[~] └─$cat /etc/security/limits.conf | grep -v ^# | grep -v ^$ @kiosk soft maxlogins 2 @kiosk hard maxlogins 2 ┌──[root@liruilongs.github.io]-[~] └─$1.2.3.4.5.6.

涉及到内存相关的资源限制:

memlock:最大锁定内存地址空间限制(以 KB 为单位)rss:最大常驻集大小限制(以 KB 为单位)物理内存stack:最大堆栈大小限制(以 KB 为单位)as:地址空间限制(以 KB 为单位)虚拟内存msgqueue:POSIX 消息队列使用的最大内存限制(以字节为单位)

配置虚拟内存限制可以通过 ulimit 进行会话基本的虚拟内存设置,下面是一个 Demo,仅对 当前 Shell 及其子进程 生效 as 虚拟地址空间限制,以 KB 为单位。

复制
┌──[root@liruilongs.github.io]-[~] └─$ulimit -v 8186 # 配置 当前 ulimit 大小为 8MB ┌──[root@liruilongs.github.io]-[~] └─$ls ls: error while loading shared libraries: libc.so.6: failed to map segment from shared object1.2.3.4.5.

修改 as 的大小之后,提示 ls 命令无法加载共享库 libc.so.6,并且无法从共享对象映射段。

永久配置(全局或用户级)需要修改 /etc/security/limits.conf, 感兴趣小伙伴可以看看我之前的博文,生效条件:用户重新登录后生效。

复制
# 格式:<domain> <type> <item> <value> liruilong hard as 819200 # Hard Limit 800MB liruilong soft as 409600 # Soft Limit 400MB1.2.3.

其他的一些限制项,也可以通过上面的方式进行,比如内存锁定,最大栈,数据段等内存相关的限制。

内核参数

通过内核参数对内存的限制主要是一些缓存区的内存占用限制,以及部分 OOM 和 内存使用策略的修改,内存页分配限制策略等。

这里关于怎么修改内核参数以及内核参数的加载方式,可以参考我之前的博文,这里就不再赘述了。

缓存区内存限制

下面为通过关键字过滤部分的内核参数。

复制
[root@developer ~]# sysctl -a | grep mem kernel.bind_memcg_blkcg_enable = 1 net.core.optmem_max = 81920 net.core.rmem_default = 212992 net.core.rmem_max = 212992 net.core.wmem_default = 212992 net.core.wmem_max = 212992 net.ipv4.fib_sync_mem = 524288 net.ipv4.igmp_max_memberships = 20 net.ipv4.tcp_mem = 78777105039157554 net.ipv4.tcp_rmem = 40961310726291456 net.ipv4.tcp_wmem = 4096163844194304 net.ipv4.udp_mem = 157557210078315114 net.ipv4.udp_rmem_min = 4096 net.ipv4.udp_wmem_min = 4096 vm.hugepage_pmem_allocall = 0 vm.hugetlb_optimize_vmemmap = 0 vm.lowmem_reserve_ratio = 256256320 vm.memcg_qos_enable = 0 vm.memcg_swap_qos_enable = 0 vm.memory_failure_early_kill = 0 vm.memory_failure_recovery = 1 vm.nr_hugepages_mempolicy = 0 vm.overcommit_memory = 0 [root@developer ~]#1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.

缓存区的话,一般用的比较多的是网络方面的,比如 TCP,UDP,Stock 等。这部分参数没有固定的值,一般根据机器使用场景动态设置。

在上面的输出中,前部位为 socket 级别的网络缓存区限制,socket接受和发送数据的缓存的最大值,这里的配置往往结合 BDP 进行配置,感兴趣小伙伴可以看看我之前网络调优的博文。

复制
net.core.optmem_max = 81920 net.core.rmem_default = 212992 net.core.rmem_max = 212992 net.core.wmem_default = 212992 net.core.wmem_max = 2129921.2.3.4.5.

后部分为 TCP/UDP 级别的网络缓存区限制,TCP 缓冲区的大小应根据系统和网络的需求进行调整。较大的缓冲区可以提高网络性能,特别是在高负载或高延迟的网络环境中。但是,过大的缓冲区可能会导致内存占用增加或延迟问题。

net.ipv4.tcp_rmem 和 net.ipv4.tcp_wmem 用于配置 TCP 套接字的接收缓冲区和发送缓冲区的大小。

复制
net.ipv4.tcp_rmem = 40961310726291456 net.ipv4.tcp_wmem = 4096163844194304 net.ipv4.udp_rmem_min = 4096 net.ipv4.udp_wmem_min = 40961.2.3.4.

下面的两个为 系统级别内存限制,单位是Page 内存页,4K,分别代表了TCP和UDP的系统层面内存限制的值,即网络连接的内存分配,包括三列:min,pressure,max。

复制
net.ipv4.tcp_mem = 78777 105039 157554 net.ipv4.udp_mem = 157557 210078 3151141.2.

这里格式有点问题,我们换一个方式查看。

复制
[root@developer ~]# cat /proc/sys/net/ipv4/tcp_mem 78777 105039 157554 [root@developer ~]#1.2.3.
内存超售限制

下面这组内核参数用于限制内存的超售问题,内存的分配和使用是两个阶段,在分配的时候是虚拟内存,而且实际使用才会分配物理内存,对于虚拟内存的分配,可以是一个很大的值,但是物理内存的分配,是有限制的,如果分配的虚拟内存大于物理内存,那么就会导致内存超售,那么这个时候,就需要限制内存的超售问题,避免内存超售导致系统崩溃。

复制
[root@developer ~]# sysctl -a | grep overcomm vm.nr_overcommit_hugepages = 0 vm.overcommit_kbytes = 0 vm.overcommit_memory = 0 vm.overcommit_ratio = 501.2.3.4.5.

vm.overcommit_memory = 0 :控制内核的内存超分配策略,决定是否允许进程申请超过物理内存 + Swap 的空间。

0(默认):启发式超分配(Heuristic Overcommit)。内核根据当前空闲内存、可回收缓存(PageCache/SLAB)和 Swap 综合判断是否允许分配。若申请量显著超过可用资源则拒绝。1:无条件允许超分配(Always Overcommit)。来者不拒,但可能因实际内存不足触发 OOM Killer。2:禁止超分配(Never Overcommit)。严格限制分配总量 ≤ (物理内存 × overcommit_ratio%) + Swap。

vm.overcommit_ratio = 50: 当 overcommit_memory=2 时,定义物理内存的可超配比例(默认值 50%)。  计算公式:  CommitLimit = (物理内存 × overcommit_ratio / 100) + Swap 。

vm.overcommit_kbytes = 0:与 overcommit_ratio 互斥,直接指定超配的字节级上限(优先级高于 ratio)。

vm.nr_overcommit_hugepages = 0:控制标准大页(HugePages)的超配数量,允许临时分配超出 vm.nr_hugepages 预设值的大页。

上面讲到了大页,这里我们顺便看看内存页相关的内存限制。

内存页限制

hugepages 用于限制分配的大页数量,这里的大页指的是标准大页,即 2MB 的大页。

复制
sysctl -w vm.nr_hugepages=1024 # 分配1024个大页(默认2MB/页)1.

如果需要自定义大页的大小,比如 1GB 的大页,需要通过 hugepagesz 来进行限制,一般启动时通过GRUB配置。

复制
hugepagesz=1G hugepages=4 default_hugepagesz=1G # 分配4个1GB大页1.

关于内存页,内核相关的参数中还有透明大页的配置,比如透明大页的开启,khugepaged 进程的扫描频率等等,感兴趣的小伙伴可以看看我之前关于大页的博文。

下面为脏页/换页/内存回收与保留相关参数的内存限制。

脏页指内存中已被修改但未写入磁盘的数据页。内核通过以下参数控制其回写行为:

复制
[root@developer ~]# sysctl -a | grep dirty vm.dirty_background_bytes = 0 # 触发后台异步刷盘的脏页绝对字节阈值(优先级高于ratio)。0表示未启用,使用dirty_background_ratio控制 vm.dirty_background_ratio = 10 # 触发后台异步刷盘的脏页内存占比阈值(默认10%)。脏页超限时内核启动后台线程异步刷盘,不阻塞应用 vm.dirty_bytes = 0 # 触发同步阻塞刷盘的脏页绝对字节阈值(优先级高于ratio)。0表示未启用,使用dirty_ratio控制 vm.dirty_expire_centisecs = 3000 # 脏页在内存中的最长存活时间(默认3000=30秒)。超时后强制刷盘,减少数据丢失风险 vm.dirty_ratio = 30 # 触发同步阻塞刷盘的脏页内存占比阈值(默认30%)。超限时应用写入被阻塞,直至脏页落盘 vm.dirty_writeback_centisecs = 500 # 内核刷盘线程的唤醒间隔(默认500=5秒)。值越小刷盘越频繁,过高可能导致I/O堆积 vm.dirtytime_expire_seconds = 43200 # 仅修改时间(atime/mtime)的脏元数据超时时间(默认43200=12小时)。通常无需调整1.2.3.4.5.6.7.8.

下面为交换分区使用限制:

复制
vm.memcg_swap_qos_enable = 0 # 内存控制组(memcg)的Swap服务质量(QoS)功能开关 vm.swap_extension = 0 # 扩展Swap空间的机制(通常用于特殊场景,如内存压缩) vm.swappiness = 30 # 控制内核使用Swap的倾向性(范围0-100)1.2.3.

还有两个特殊的参数需要单独说明一下:

vm.min_free_kbytes:系统保留的最小空闲内存(单位 KB),用于应对突发需求。vm.watermark_scale_factor:调整内存回收敏感度(默认 10,范围 1-1000)。
复制
[root@developer ~]# sysctl -a | grep min_free_kbytes vm.min_free_kbytes = 45056 [root@developer ~]# sysctl -a | grep watermark_scale_factor vm.watermark_scale_factor = 10 [root@developer ~]#1.2.3.4.5.
OOM 相关内存限制

下面为OOM相关,的内存限制,关于 OOM 是什么,什么原理不是本文重点,小伙伴可以看看我之前的博文。

复制
[root@developer ~]# sysctl -a | grep oom vm.enable_oom_killer = 1 # 启用OOM Killer机制(默认值)。当系统内存耗尽时,内核自动终止进程释放内存 vm.oom_dump_tasks = 1 # OOM触发时记录所有进程的内存状态到日志(/var/log/messages),便于事后分析 vm.oom_kill_allocating_task = 0 # 禁用“直接杀死触发OOM的进程”。内核会扫描所有进程,选择badness分数最高的进程终止 vm.panic_on_oom = 0 # OOM时禁止内核崩溃(panic)。0表示调用OOM Killer而非重启系统(生产环境推荐 [root@developer ~]#1.2.3.4.5.6.
NUMA内存限制
复制
[root@developer ~]# sysctl -a | grep zone_reclaim_mode vm.zone_reclaim_mode = 0 # 关闭本地NUMA节点内存强制回收。允许从其他节点分配内存,避免因本地回收降低性能 [root@developer ~]# sysctl -a | grep min_unmapped_ratio vm.min_unmapped_ratio = 1 # 每个NUMA节点保留1%的未映射内存(用于临时内存分配) [root@developer ~]#1.2.3.4.5.

对于内存资源的限制内核参数还有很多,这里只是列举了部分。

THE END