问题背景
有小伙伴反馈客户生产环境(裸机部署)频繁告警可用内存不足,但是计算出来每个进程的内存使用总和不多,那多出来的内存哪里去了呢?
通过 free 命令我们能计算出来系统的使用内存 (used=11G),free 内存 (free=4.8G)
当前 linux 系统的版本是 RHEL 6.8 内核 kernel 版本 2.6.32
top 命令里面展示的 top 进程使用的 RES 内存总和不到 6G,
剩余的 11-6=5G 哪里去了呢?
被系统内核占用了?
那怎么才能看到系统内存的详细分配情况?
带着这些疑问我们来梳理 linux 中常见的查看系统内存资源一些命令和详细的指标解释,包括一些参数配置的最佳实践
命令集
free 命令
命令
选项
-b # 以Byte为单位显示内存使用情况;
-k # 以KB为单位显示内存使用情况;
-m # 以MB为单位显示内存使用情况;
-g # 以GB为单位显示内存使用情况。
-o # 不显示缓冲区调节列;
-s<间隔秒数> # 持续观察内存使用状况;
-t # 显示内存总和列;
-V # 显示版本信息。
复制代码
数据
free -m
total used free shared buffers cached
Mem: 2016 1973 42 0 163 1497
-/+ buffers/cache: 312 1703
Swap: 4094 0 4094
复制代码
注意:RHEL 6 和 RHEL 7 及以上版本可能还不一样,7 以上版本会多一项数据指标 avaliable,会帮你把真实可用的内存值算出来。
含义解释
Mem 行显示的数值定义
total:系统内存总数
used:已经使用的内存数
free: 空闲的内存数
shared:当前已经废弃不用
buffers:缓存内存数
cached:缓存内存数
Buffers 和 cached 的区别
Buffers
作为 buffer cache 的内存,是块设备的读写缓冲区,是即将写入磁盘的,是内存和磁盘之间的缓存
buffer 是由各种进程分配的,被用在如输入队列等方面。一个简单的例子如某个进程要求有多个字段读入,在所有字段被读入完整之前,进程把先前读入的字段放在 buffer 中保存
cached
作为 page cache 的内存, 文件系统的 cache,是 cpu 到内存之间的缓存,是从磁盘中读出来的数据的缓存
cache 经常被用在磁盘的 I/O 请求上,如果有多个进程都要访问某个文件,于是该文件便被做成 cache 以方便下次被访问,这样可提高系统性能
如何释放缓存
#释放page cache
echo 1 > /proc/sys/vm/drop_caches
----------------------------------------
#释放 Linux 系统中的 dentries(目录项)和 inodes(索引节点)所占用的资源
echo 2 > /proc/sys/vm/drop_caches
----------------------------------------
#释放 page cache 及 dentries(目录项)和 inodes(索引节点)所占用的资源
echo 3 > /proc/sys/vm/drop_caches
----------------------------------------
#释放前最好sync一下,防止丢失数据,但是一般情况下没有必要手动释放内存
复制代码
(-/+ buffers/cache)是从用户角度描述内存使用详情
used = Mem 行的 used-buffers-cached
free = Mem 行的 free+buffers+cached
SWAP
在 Linux 系统中,Swap 是一种虚拟内存技术,用于扩展系统的可用内存空间。当物理内存(RAM)不足时,Swap 允许将一部分数据从内存交换到硬盘上的 Swap 分区或 Swap 文件中
SWAP 的优势
扩展内存:Swap 提供了一种方式来增加系统可用的虚拟内存空间,以处理内存需求超过物理内存容量的情况
保证系统的稳定:当物理内存不足时,Swap 可以避免系统崩溃或进程被终止,而是将一部分数据交换到磁盘上
SWAP 的值设置的最佳实践
针对小型的系统物理内存的倍数:一般来说,建议将 Swap 的大小设置为物理内存的 1-2 倍
对于大型的系统可以设置为内存的一半:如 16G 的物理内存,可以把 SWAP 设置为 8G
考虑应用系统的类型,对于内存密集型的系统,建议把 SWAP 值尽量提高
保留系统的冗余能力
linux 上查看 Swap 设置值大小和修改方法
查看
#查看方式1
swapon --show
---------------------------------------------
NAME TYPE SIZE USED PRIO
/dev/dm-1 partition 15.7G 12G -2
---------------------------------------------
#查看方式2
cat /etc/fstab
--------------------------------------------
/dev/mapper/centos-root / xfs defaults 0 0
UUID=5daa0a7f-cc95-4a7d-9aeb-94816daf4025 /boot xfs defaults 0 0
/dev/mapper/centos-home /home xfs defaults 0 0
/dev/mapper/centos-swap swap swap defaults 0 0
---------------------------------------------
# swap 和dm-1建立的是软连接
---------------------------------------------
lrwxrwxrwx. 1 root root 7 5月 22 20:10 centos-swap -> ../dm-1
---------------------------------------------
复制代码
#创建 Swap 分区(可选)
---------------------------------------------
sudo mkswap /dev/mapper/test-swap
---------------------------------------------
#方法一
---------------------------------------------
sudo swapon /dev/mapper/test-swap
---------------------------------------------
#方法二(永久设置 Swap 分区)
#编辑上文中的/etc/fstab 文件,并添加以下行到文件末尾
---------------------------------------------
vim /etc/fstab
---------------------------------------------
#添加代码
---------------------------------------------
/dev/mapper/test-swap none swap defaults 0 0
---------------------------------------------
复制代码
#关闭swap
swapoff /dev/mapper/test-swap
复制代码
修改触发 swap 的阈值 (定义:内存在使用到 100-swappiness 的时候,就开始出现有交换分区的使用)
0:在任何情况下都不发生物理内存数据和 swap 文件的交换
100:表示积极进行物理内存数据和 swap 数据的交换
#查看阈值
cat /proc/sys/vm/swappiness
---------------------------------------------
30
---------------------------------------------
#临时修改
sysctl vm.swappiness=60
#永久修改
vim /etc/sysctl.conf
#添加
vm.swappiness=60
复制代码
物理内存和 Swap 使用的优先级和策略
物理内存优先
当物理内存不足时,系统会将一部分不活动的数据(如未使用的进程、未被频繁访问的页面等)交换到 Swap 分区或交换文件中
Linux 内核中使用的内存管理算法会根据应用程序的需求和系统的负载情况来决定何时将数据交换到 Swap
Swappiness 值
Swap 中的数据何时回返还到物理内存中
内存需求减少
通过一些计算算法,发现部分频繁访问的数据在 swap 区,而物理内存中存在不经常被访问的数据,这个时候系统会做热点数据的交换,以保证获得更高的数据读取性能
Top 命令
top 命令面板
指标解析
load average: 如果这个数除以逻辑 CPU 的数量,结果高于 5 的时候就表明系统在超负荷运转了。
|
默认情况下仅显示比较重要的 PID、USER、PR、NI、VIRT、RES、SHR、S、%CPU、%MEM、TIME+、COMMAND 几个列!
如果想看上述列表中的其他数据,先按键 f ,再选择你想展示的指标 ,按 d 选中 回车。q 退出
top
#按 f
------------------------------------------------------------------
* PID = Process Id RSfd = RES File-based (KiB)
* USER = Effective User Name RSlk = RES Locked (KiB)
* PR = Priority RSsh = RES Shared (KiB)
* NI = Nice Value CGNAME = Control Group name
* VIRT = Virtual Image (KiB) NU = Last Used NUMA node
* RES = Resident Size (KiB)
* SHR = Shared Memory (KiB)
* S = Process Status
* %CPU = CPU Usage
* %MEM = Memory Usage (RES)
* TIME+ = CPU Time, hundredths
* COMMAND = Command Name/Line
PPID = Parent Process pid
UID = Effective User Id
RUID = Real User Id
RUSER = Real User Name
SUID = Saved User Id
SUSER = Saved User Name
GID = Group Id
GROUP = Group Name
PGRP = Process Group Id
TTY = Controlling Tty
TPGID = Tty Process Grp Id
SID = Session Id
nTH = Number of Threads
P = Last Used Cpu (SMP)
TIME = CPU Time
SWAP = Swapped Size (KiB)
CODE = Code Size (KiB)
DATA = Data+Stack (KiB)
nMaj = Major Page Faults
nMin = Minor Page Faults
nDRT = Dirty Pages Count
WCHAN = Sleeping in Function
Flags = Task Flags <sched.h>
CGROUPS = Control Groups
SUPGIDS = Supp Groups IDs
SUPGRPS = Supp Groups Names
TGID = Thread Group Id
OOMa = OOMEM Adjustment
OOMs = OOMEM Score current
ENVIRON = Environment vars
vMj = Major Faults delta
vMn = Minor Faults delta
USED = Res+Swap Size (KiB)
nsIPC = IPC namespace Inode
nsMNT = MNT namespace Inode
nsNET = NET namespace Inode
nsPID = PID namespace Inode
nsUSER = USER namespace Inode
nsUTS = UTS namespace Inode
LXC = LXC container name
RSan = RES Anonymous (KiB)
------------------------------------------------------------------
# 选中想要展示的指标 按d 选中 按q 退出
------------------------------------------------------------------
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND PPID TTY CODE DATA
6218 systemd+ 20 0 1740884 404556 0 S 0.3 10.5 420:03.84 mysqld 6201 ? 55828 806684
6768 root 20 0 2736204 410092 3368 S 0.3 10.6 103:43.46 java 6750 pts/0 4 615804
1 root 20 0 178784 10308 7468 S 0.0 0.3 172:31.49 systemd 0 ? 1260 20720
2 root 20 0 0 0 0 S 0.0 0.0 0:03.22 kthreadd 0 ? 0 0
3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_gp 2 ? 0 0
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_par_gp 2 ? 0 0
6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H-kblockd 2 ? 0 0
8 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_percpu_wq 2 ? 0 0
9 root 20 0 0 0 0 S 0.0 0.0 2:54.83 ksoftirqd/0 2 ? 0 0
10 root 20 0 0 0 0 I 0.0 0.0 26:28.63 rcu_sched 2 ? 0 0
11 root rt 0 0 0 0 S 0.0 0.0 0:05.90 migration/0 2 ? 0 0
12 root rt 0 0 0 0 S 0.0 0.0 0:00.60 watchdog/0 2 ? 0 0
13 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cpuhp/0 2 ? 0 0
14 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cpuhp/1 2 ? 0 0
复制代码
常用命令
top + c 显示启动命令
top + m 按照内存使用从高到低进程排序
top + P 按照 cpu 使用从高到低进程排序
top + 1 显示每个 cpu 的使用情况
重点指标
RES(Resident Set Size):RES 表示进程当前实际占用的物理内存大小。它包括进程的代码、数据和共享库等在物理内存中的部分。RES 反映了进程实际使用的物理内存量,也可以理解为进程当前使用的实际内存。RES 的值通常大于 DATA+CODE 的值
VIRT(Virtual Memory Size):VIRT 表示进程所占用的虚拟内存大小。它包括进程可访问的所有虚拟内存空间,包括实际分配的内存、共享库、堆、栈和映射文件等。VIRT 指标可能会大于实际物理内存,因为它包括了未实际分配的虚拟内存空间
VIRT 包含 RES 的值
区别
meminfo
面板
cat /proc/meminfo
----------------------------------------------------------------
MemTotal: 3868864 kB
MemFree: 140928 kB
MemAvailable: 2456296 kB
Buffers: 210416 kB
Cached: 2238404 kB
SwapCached: 0 kB
Active: 2546324 kB
Inactive: 941624 kB
Active(anon): 1038396 kB
Inactive(anon): 1836 kB
Active(file): 1507928 kB
Inactive(file): 939788 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 1000 kB
Writeback: 0 kB
AnonPages: 1017048 kB
Mapped: 188840 kB
Shmem: 2216 kB
KReclaimable: 144468 kB
Slab: 195904 kB
SReclaimable: 144468 kB
SUnreclaim: 51436 kB
KernelStack: 4352 kB
PageTables: 10872 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 1934432 kB
Committed_AS: 2446620 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
Percpu: 1328 kB
HardwareCorrupted: 0 kB
AnonHugePages: 784384 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 171896 kB
DirectMap2M: 4022272 kB
DirectMap1G: 2097152 kB
复制代码
指标解释
重要指标
Committed_AS:目前在系统上分配的内存量。是所有进程申请的内存的总和
Slab:内核数据结构缓存的大小,可以减少申请和释放内存带来的消耗
SReclaimable:可收回 Slab 的大小
PageTables:管理内存分页页面的索引表的大小
SLAB 是什么
内核为了高性能每个需要重复使用的对象都会有个池,这个 slab 池会 cache 大量常用的对象,所以会消耗大量的内存,他所消耗的内存是算在 free 命令中的 used 中还是在 cache 中呢?我们通过一组命令来确认
slabtop
#展示具体内核使用的内存的明细
slabtop
--------------------------------------------------------------------------------
Active / Total Objects (% used) : 3774607 / 4716484 (80.0%)
Active / Total Slabs (% used) : 86026 / 86026 (100.0%)
Active / Total Caches (% used) : 92 / 119 (77.3%)
Active / Total Size (% used) : 678448.10K / 829043.07K (81.8%)
Minimum / Average / Maximum Object : 0.01K / 0.17K / 14.09K
OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
2262372 2262372 100% 0.19K 53866 42 430928K dentry
436224 417418 95% 0.03K 3408 128 13632K kmalloc-32
401856 144870 36% 0.06K 6279 64 25116K kmalloc-64
337920 105054 31% 0.02K 1320 256 5280K kmalloc-16
269688 80643 29% 0.04K 2644 102 10576K selinux_inode_security
137360 137360 100% 0.02K 808 170 3232K fsnotify_mark_connector
126350 44070 34% 0.57K 2636 56 84352K radix_tree_node
115712 110541 95% 0.01K 226 512 904K kmalloc-8
59920 55324 92% 0.21K 1621 37 12968K vm_area_struct
52952 14489 27% 0.94K 1568 34 50176K xfs_inode
52836 52836 100% 0.12K 777 68 6216K kernfs_node_cache
47872 39820 83% 0.25K 748 64 11968K kmalloc-256
--------------------------------------------------------------------------------
#或者使用
cat /proc/slabinfo
--------------------------------------------------------------------------------
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
nf_conntrack_ffff8d47c7815200 2550 2550 320 51 4 : tunables 0 0 0 : slabdata 50 50 0
nf_conntrack_ffff8d49b5e39480 306 306 320 51 4 : tunables 0 0 0 : slabdata 6 6 0
nf_conntrack_ffff8d47c7813d80 2424 2652 320 51 4 : tunables 0 0 0 : slabdata 52 52 0
nf_conntrack_ffff8d486c7f5200 2961 3366 320 51 4 : tunables 0 0 0 : slabdata 66 66 0
nf_conntrack_ffff8d47c7812900 612 612 320 51 4 : tunables 0 0 0 : slabdata 12 12 0
nf_conntrack_ffff8d49cbaebd80 306 306 320 51 4 : tunables 0 0 0 : slabdata 6 6 0
nf_conntrack_ffff8d486a449480 306 306 320 51 4 : tunables 0 0 0 : slabdata 6 6 0
nf_conntrack_ffff8d486a44bd80 2346 2346 320 51 4 : tunables 0 0 0 : slabdata 46 46 0
nf_conntrack_ffff8d4877072900 2149 2346 320 51 4 : tunables 0 0 0 : slabdata 46 46 0
nf_conntrack_ffff8d486a44e680 459 459 320 51 4 : tunables 0 0 0 : slabdata 9 9 0
nf_conntrack_ffff8d486a44d200 2958 2958 320 51 4 : tunables 0 0 0 : slabdata 58 58 0
nf_conntrack_ffff8d49cbaee680 306 306 320 51 4 : tunables 0 0 0 : slabdata 6 6 0
nf_conntrack_ffff8d4877076680 255 255 320 51 4 : tunables 0 0 0 : slabdata 5 5 0
nf_conntrack_ffff8d486a44a900 51 51 320 51 4 : tunables 0 0 0 : slabdata 1 1 0
nf_conntrack_ffff8d49cbaed200 357 357 320 51 4 : tunables 0 0 0 : slabdata 7 7 0
nf_conntrack_ffffffffba912940 2703 2703 320 51 4 : tunables 0 0 0 : slabdata 53 53 0
--------------------------------------------------------------------------------
复制代码
PageTables
echo `grep PageTables /proc/meminfo | awk '{print $2}'`
复制代码
struct page 是系统 boot 的时候就会根据内存大小算出来分配出去的,18 内核是 1.56%左右,32 内核由于 cgroup 的原因会在 2.3%
单进程内存使用详情
cat /proc/PID/status
------------------------------------------------------------------------------
Name: mysqld
Umask: 0026
State: S (sleeping)
Tgid: 6218
Ngid: 0
Pid: 6218
PPid: 6201
TracerPid: 0
Uid: 999 999 999 999
Gid: 999 999 999 999
FDSize: 256
Groups:
NStgid: 6218 1
NSpid: 6218 1
NSpgid: 6218 1
NSsid: 6218 1
VmPeak: 1784400 kB
VmSize: 1740884 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 418632 kB
VmRSS: 404556 kB
RssAnon: 404556 kB
RssFile: 0 kB
RssShmem: 0 kB
VmData: 806552 kB
VmStk: 132 kB
VmExe: 31092 kB
VmLib: 0 kB
VmPTE: 1168 kB
VmSwap: 0 kB
------------------------------------------------------------------------------
复制代码
进程直接内存的使用
#pmap命令可以查看进程的内存映射,包括堆外内存的使用量
pmap -x PID
----------------------------------------------------------------
00000000f0000000 267776 155072 155072 rw--- [ anon ]
0000000100580000 1042944 0 0 ----- [ anon ]
000055c0b1b3b000 4 0 0 r-x-- java
000055c0b1d3b000 4 4 4 r---- java
000055c0b1d3c000 4 4 4 rw--- java
000055c0b3499000 1696 1460 1460 rw--- [ anon ]
00007f4fdeeb9000 12 0 0 ----- [ anon ]
00007f4fdeebc000 1016 24 24 rw--- [ anon ]
----------------------------------------------------------------
cat /proc/PID/smaps
----------------------------------------------------------------
Size: 267776 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 155072 kB
Pss: 155072 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 155072 kB
Referenced: 155072 kB
Anonymous: 155072 kB
LazyFree: 0 kB
AnonHugePages: 153600 kB
ShmemPmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
VmFlags: rd wr mr mw me ac sd
100580000-140000000 ---p 00000000 00:00 0
复制代码
AnonHugePages、Locked、RssAnon 等字段的行,它们提供了关于堆外内存的使用量的信息
不同维度的内存监控数据之间的关系
Committed_AS 包含了虚拟内存,所以 committed_AS 的值可能大于 free 命令中的 used 的值
VmRSS 表示当前实际驻留在物理内存中的内存大小,而 RES 表示进程当前使用的物理内存总量。两者之间的 关系是,VmRSS 是 RES 中的一个组成部分,两者相差不大,基本相同
free 中 used 的内存都去哪儿
通过该工具我们能看到内核使用的内存及用户态使用的内存
USED = use(用户态内存)+ Slab + PageTables
我们来做个实验
RSS 基本等于 VmRSS ,那我们通过 top 命令统计出所有的用户进程占用的物理内存
vi rss.sh
------------------------------------------------------------
#/bin/bash
for PROC in `ls /proc/|grep "^[0-9]"`
do
if [ -f /proc/$PROC/statm ]; then
TEP=`cat /proc/$PROC/statm | awk '{print ($2)}'`
RSS=`expr $RSS + $TEP`
fi
done
RSS=`expr $RSS \* 4`
echo $RSS"KB"
------------------------------------------------------------
sh RSS.sh
复制代码
echo `cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'` MB
复制代码
echo `grep PageTables /proc/meminfo | awk '{print $2}'` KB
复制代码
通过上面的结论,当然数据上可能稍微有一些出入,特别是不同的 kernel 版本计算的方法可能存在一些差异,但是我们基本可以确定的是上述 used 的计算公式基本成立
解决方案
echo 3 > /proc/sys/vm/drop_caches
复制代码
讨论
JVM 内存设置参数中中通常有设置 xms 和 xmx,如果 xms=xmx ,那么如果 jvm 使用的物理内存没达到 xms 设置的值,used 中展示的是真实的内存使用值还是 xms 的值。如果是显示的是真实使用的物理内存的值,那么 xms 设置还有什么意义。怎么保证 jvm 在需要申请内存的时候有不少于 xms 的内存能够被申请到
如果大家感兴趣在下期分享
评论