写点什么

内存都去哪了

作者:BeyondLife
  • 2023-09-18
    江苏
  • 本文字数:10400 字

    阅读完需:约 34 分钟

内存都去哪了

问题背景



有小伙伴反馈客户生产环境(裸机部署)频繁告警可用内存不足,但是计算出来每个进程的内存使用总和不多,那多出来的内存哪里去了呢?


  • 通过 free 命令我们能计算出来系统的使用内存 (used=11G),free 内存 (free=4.8G)

  • 当前 linux 系统的版本是 RHEL 6.8 内核 kernel 版本 2.6.32

  • top 命令里面展示的 top 进程使用的 RES 内存总和不到 6G,

  • 剩余的 11-6=5G 哪里去了呢?

  • 被系统内核占用了?

  • 那怎么才能看到系统内存的详细分配情况?


带着这些疑问我们来梳理 linux 中常见的查看系统内存资源一些命令和详细的指标解释,包括一些参数配置的最佳实践

命令集

free 命令

命令

free -m
复制代码

选项

-b # 以Byte为单位显示内存使用情况;-k # 以KB为单位显示内存使用情况;-m # 以MB为单位显示内存使用情况;-g # 以GB为单位显示内存使用情况。 -o # 不显示缓冲区调节列;-s<间隔秒数> # 持续观察内存使用状况;-t # 显示内存总和列;-V # 显示版本信息。
复制代码

数据

free -m             total       used       free     shared    buffers     cachedMem:          2016       1973         42          0        163       1497-/+ buffers/cache:        312       1703Swap:         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 cacheecho 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 设置值大小和修改方法

  • 查看


#查看方式1swapon --show---------------------------------------------NAME      TYPE       SIZE USED PRIO/dev/dm-1 partition 15.7G  12G   -2---------------------------------------------#查看方式2cat /etc/fstab--------------------------------------------/dev/mapper/centos-root /                       xfs     defaults        0 0UUID=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


#关闭swapswapoff /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 的时候就表明系统在超负荷运转了。


  • 第二行



  • 第三行


|


  • 第四行第五行和内存使用相关的数据和 free 命令中的一致



  • 进程相关



默认情况下仅显示比较重要的 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 的使用情况

重点指标

  1. RES(Resident Set Size):RES 表示进程当前实际占用的物理内存大小。它包括进程的代码、数据和共享库等在物理内存中的部分。RES 反映了进程实际使用的物理内存量,也可以理解为进程当前使用的实际内存。RES 的值通常大于 DATA+CODE 的值

  2. VIRT(Virtual Memory Size):VIRT 表示进程所占用的虚拟内存大小。它包括进程可访问的所有虚拟内存空间,包括实际分配的内存、共享库、堆、栈和映射文件等。VIRT 指标可能会大于实际物理内存,因为它包括了未实际分配的虚拟内存空间


VIRT 包含 RES 的值


区别


  • RES 反映了进程实际使用的物理内存量,而 VIRT 则表示进程所占用的虚拟内存大小。

  • RES 是实际分配到物理内存的部分,而 VIRT 包括了所有可访问的虚拟内存空间。

meminfo

面板

cat /proc/meminfo----------------------------------------------------------------MemTotal:        3868864 kBMemFree:          140928 kBMemAvailable:    2456296 kBBuffers:          210416 kBCached:          2238404 kBSwapCached:            0 kBActive:          2546324 kBInactive:         941624 kBActive(anon):    1038396 kBInactive(anon):     1836 kBActive(file):    1507928 kBInactive(file):   939788 kBUnevictable:           0 kBMlocked:               0 kBSwapTotal:             0 kBSwapFree:              0 kBDirty:              1000 kBWriteback:             0 kBAnonPages:       1017048 kBMapped:           188840 kBShmem:              2216 kBKReclaimable:     144468 kBSlab:             195904 kBSReclaimable:     144468 kBSUnreclaim:        51436 kBKernelStack:        4352 kBPageTables:        10872 kBNFS_Unstable:          0 kBBounce:                0 kBWritebackTmp:          0 kBCommitLimit:     1934432 kBCommitted_AS:    2446620 kBVmallocTotal:   34359738367 kBVmallocUsed:           0 kBVmallocChunk:          0 kBPercpu:             1328 kBHardwareCorrupted:     0 kBAnonHugePages:    784384 kBShmemHugePages:        0 kBShmemPmdMapped:        0 kBHugePages_Total:       0HugePages_Free:        0HugePages_Rsvd:        0HugePages_Surp:        0Hugepagesize:       2048 kBHugetlb:               0 kBDirectMap4k:      171896 kBDirectMap2M:     4022272 kBDirectMap1G:     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 dentry436224 417418 95% 0.03K 3408 128 13632K kmalloc-32401856 144870 36% 0.06K 6279 64 25116K kmalloc-64337920 105054 31% 0.02K 1320 256 5280K kmalloc-16269688 80643 29% 0.04K 2644 102 10576K selinux_inode_security137360 137360 100% 0.02K 808 170 3232K fsnotify_mark_connector126350 44070 34% 0.57K 2636 56 84352K radix_tree_node115712 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 0nf_conntrack_ffff8d49b5e39480 306 306 320 51 4 : tunables 0 0 0 : slabdata 6 6 0nf_conntrack_ffff8d47c7813d80 2424 2652 320 51 4 : tunables 0 0 0 : slabdata 52 52 0nf_conntrack_ffff8d486c7f5200 2961 3366 320 51 4 : tunables 0 0 0 : slabdata 66 66 0nf_conntrack_ffff8d47c7812900 612 612 320 51 4 : tunables 0 0 0 : slabdata 12 12 0nf_conntrack_ffff8d49cbaebd80 306 306 320 51 4 : tunables 0 0 0 : slabdata 6 6 0nf_conntrack_ffff8d486a449480 306 306 320 51 4 : tunables 0 0 0 : slabdata 6 6 0nf_conntrack_ffff8d486a44bd80 2346 2346 320 51 4 : tunables 0 0 0 : slabdata 46 46 0nf_conntrack_ffff8d4877072900 2149 2346 320 51 4 : tunables 0 0 0 : slabdata 46 46 0nf_conntrack_ffff8d486a44e680 459 459 320 51 4 : tunables 0 0 0 : slabdata 9 9 0nf_conntrack_ffff8d486a44d200 2958 2958 320 51 4 : tunables 0 0 0 : slabdata 58 58 0nf_conntrack_ffff8d49cbaee680 306 306 320 51 4 : tunables 0 0 0 : slabdata 6 6 0nf_conntrack_ffff8d4877076680 255 255 320 51 4 : tunables 0 0 0 : slabdata 5 5 0nf_conntrack_ffff8d486a44a900 51 51 320 51 4 : tunables 0 0 0 : slabdata 1 1 0nf_conntrack_ffff8d49cbaed200 357 357 320 51 4 : tunables 0 0 0 : slabdata 7 7 0nf_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:   mysqldUmask:  0026State:  S (sleeping)Tgid:   6218Ngid:   0Pid:    6218PPid:   6201TracerPid:     0Uid:    999    999      999     999Gid:    999    999      999     999FDSize: 256Groups:  NStgid: 6218   1NSpid:  6218   1NSpgid: 6218   1NSsid:  6218   1VmPeak:  1784400 kBVmSize:  1740884 kBVmLck:         0 kBVmPin:         0 kBVmHWM:    418632 kBVmRSS:    404556 kBRssAnon:         404556 kBRssFile:              0 kBRssShmem:             0 kBVmData:   806552 kBVmStk:       132 kBVmExe:     31092 kBVmLib:         0 kBVmPTE:      1168 kBVmSwap:        0 kB------------------------------------------------------------------------------
复制代码

进程直接内存的使用

#pmap命令可以查看进程的内存映射,包括堆外内存的使用量pmap -x PID----------------------------------------------------------------00000000f0000000  267776  155072  155072 rw---   [ anon ]0000000100580000 1042944       0       0 -----   [ anon ]000055c0b1b3b000       4       0       0 r-x-- java000055c0b1d3b000       4       4       4 r---- java000055c0b1d3c000       4       4       4 rw--- java000055c0b3499000    1696    1460    1460 rw---   [ anon ]00007f4fdeeb9000      12       0       0 -----   [ anon ]00007f4fdeebc000    1016      24      24 rw---   [ anon ]----------------------------------------------------------------cat /proc/PID/smaps----------------------------------------------------------------Size:             267776 kBKernelPageSize:        4 kBMMUPageSize:           4 kBRss:              155072 kBPss:              155072 kBShared_Clean:          0 kBShared_Dirty:          0 kBPrivate_Clean:         0 kBPrivate_Dirty:    155072 kBReferenced:       155072 kBAnonymous:        155072 kBLazyFree:              0 kBAnonHugePages:    153600 kBShmemPmdMapped:        0 kBShared_Hugetlb:        0 kBPrivate_Hugetlb:       0 kBSwap:                  0 kBSwapPss:               0 kBLocked:                0 kBVmFlags: rd wr mr mw me ac sd 100580000-140000000 ---p 00000000 00:00 0 
复制代码


AnonHugePages、Locked、RssAnon 等字段的行,它们提供了关于堆外内存的使用量的信息

不同维度的内存监控数据之间的关系

  • Committed_AS 和 used 之间的关系


Committed_AS 包含了虚拟内存,所以 committed_AS 的值可能大于 free 命令中的 used 的值


  • VmRSS 和 RES 的关系

  • VmRSS(Virtual Memory Resident Set Size)是指进程在物理内存中实际驻留的内存大小,即进程当前使用的实际物理内存量。

  • RES(Resident Set Size)是指进程当前使用的物理内存量,包括驻留在物理内存中的代码、数据和共享内存等。


VmRSS 表示当前实际驻留在物理内存中的内存大小,而 RES 表示进程当前使用的物理内存总量。两者之间的 关系是,VmRSS 是 RES 中的一个组成部分,两者相差不大,基本相同

free 中 used 的内存都去哪儿

  • 实验版本:Linux version 3.10.0-1062.4.1.el7.x86_64

  • 首先我们介绍一个工具 nmon (yum install nmon),是一款比较好用的性能分析工具

  • 通过该工具我们能看到内核使用的内存及用户态使用的内存

  • 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`  fidoneRSS=`expr $RSS \* 4`echo $RSS"KB"------------------------------------------------------------sh RSS.sh
复制代码



  • free 命令



  • 通过 slab 的占用


echo `cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'` MB
复制代码



  • PageTables


echo `grep PageTables /proc/meminfo | awk '{print $2}'` KB
复制代码



通过上面的结论,当然数据上可能稍微有一些出入,特别是不同的 kernel 版本计算的方法可能存在一些差异,但是我们基本可以确定的是上述 used 的计算公式基本成立


  • 回到开头的那个问题,小伙伴又送来一张现场的截图,进一步证明了上述的计算公式是成立的



  • 那为什么 slab 占用这么多,可以考虑下是不是可以做下手动回收,服务器上有频繁的用户态到内核态的内存申请或者网络和文件的读取

解决方案

  • 释放缓存


echo 3 > /proc/sys/vm/drop_caches
复制代码


  • 重启服务器,或者升级内核版本,2.6 版本上存在 slab 内存泄露的问题

讨论

JVM 内存设置参数中中通常有设置 xms 和 xmx,如果 xms=xmx ,那么如果 jvm 使用的物理内存没达到 xms 设置的值,used 中展示的是真实的内存使用值还是 xms 的值。如果是显示的是真实使用的物理内存的值,那么 xms 设置还有什么意义。怎么保证 jvm 在需要申请内存的时候有不少于 xms 的内存能够被申请到


如果大家感兴趣在下期分享


用户头像

BeyondLife

关注

还未添加个人签名 2018-05-03 加入

十年开发老兵,多年搜索及推荐领域技术和业务积累目前主要研究低代码平台设计及开发模式,关注三高架构设计方案及系统实现,微服务治理实践及DDD领域驱动设计方案,目前致力于研究低代码平台的最佳实践方案

评论

发布
暂无评论
内存都去哪了_nmon_BeyondLife_InfoQ写作社区