在系统层面最基础最重要的三个指标是CPU、IO、memory,本篇主要汇总定位分析内存使用的常见思路。在进行下一步之前,需要先对top、free 、/proc/meminfo、slabtop、nmon等工具要有一个概念和认识。如果可能的话,最好还需要先了解下/proc/$pid下的smaps和status文件。以下为定位内存被谁占用的常见思路。

一、free和top

free命令是linux的一个入门级命令,显示的是一个比较总述性的信息,如下:

1[root@361way ~]# free -m
2             total       used       free     shared    buffers     cached
3Mem:           995        874        121          0        105        249
4-/+ buffers/cache:        518        477
5Swap:            0          0          0

比如上面的输出中,我们大致可以看到我总内存为1G(995,其中hardward和firmware在启动时会预先占用一点)。其中已使用874M ,其中可用121M,buffers和cached加用的量为105M + 249M ,这里需要注意的是平时我们在查看时,一般会以第二行的结果为准,即实际可用477M,已用518M。为什么这样说?因为buffers和cached是为了加快运算速度,会预占用一部分内存,可以理解为缓存的概念。由于这部分不是本篇的重点,想深究的可以找谷歌。这部分内存可以通过如下的命令进行回收:

1To free pagecache:
2echo 1 > /proc/sys/vm/drop_caches
3To free dentries and inodes:
4echo 2 > /proc/sys/vm/drop_caches
5To free pagecache, dentries and inodes:
6echo 3 > /proc/sys/vm/drop_caches

在有业务运行的情况下,强烈不建议这样操作,因为可能会造成数据丢失。即然内存被使用,到底被谁占去了呢?可以借助强大的top查看。

top memory
top memory

如上图所示,在top下我们输入大M就可以按内存使用率排序。上面可以看到我内存主要被hhvm进程占用掉了,占比总内存的34.3% 。可以看到,实际上top上面也有free的功能,对memory会有概述性报告的。其中RES是我们要关注的项,即实际该进程占用的内存量,基本上这样我们就定位到内存用到那去了。现网中经常还需要一种情况,top看到的所有进程的RES使用都不大,而内存一下子少了几十G,这个怎么破呢?看下面。

二、nmon 、/proc/meminfo 与slabtop

1、/proc/meminfo

meminfo文件显示出的也是内存的概述性信息,只不过其比free -m的结果要更详细,如下:

 1[root@361way ~]# cat /proc/meminfo
 2MemTotal:        1019644 kB    所有可用RAM大小 (即物理内存减去一些预留位和内核的二进
 3制代码大小)
 4MemFree:          119464 kB    LowFree与HighFree的总和,被系统留着未使用的内存
 5Buffers:          110680 kB    用来给文件做缓冲大小
 6Cached:           256796 kB    被高速缓冲存储器(cache memory)用的内存的大小(等于
 7diskcache + SwapCache )
 8SwapCached:            0 kB   
 9Active:           680560 kB  在活跃使用中的缓冲或高速缓冲存储器页面文件的大小,除非
10非常必要否则不会被移作他用
11Inactive:         145228 kB    在不经常使用中的缓冲或高速缓冲存储器页面文件的大小,可
12能被用于其他途径
13Active(anon):     458208 kB
14Inactive(anon):      280 kB
15Active(file):     222352 kB
16Inactive(file):   144948 kB
17Unevictable:           0 kB
18Mlocked:               0 kB
19SwapTotal:             0 kB
20SwapFree:              0 kB
21Dirty:                92 kB   等待被写回到磁盘的内存大小
22Writeback:             0 kB   正在被写回到磁盘的内存大小
23AnonPages:        458328 kB   未映射页的内存大小
24Mapped:            27072 kB   设备和文件等映射的大小
25Shmem:               176 kB
26Slab:              53564 kB   内核数据结构缓存的大小,可以减少申请和释放内存带来的消耗
27SReclaimable:      32404 kB   可收回Slab的大小
28SUnreclaim:        21160 kB   不可收回Slab的大小(SUnreclaim+SReclaimable=Slab)
29KernelStack:        1136 kB   内核栈大小占用的内存
30PageTables:         5856 kB   管理内存分页页面的索引表的大小
31NFS_Unstable:          0 kB   不稳定页表的大小
32Bounce:                0 kB
33WritebackTmp:          0 kB
34CommitLimit:      509820 kB
35Committed_AS:    1483868 kB
36VmallocTotal:   34359738367 kB  可以vmalloc虚拟内存大小
37VmallocUsed:        7472 kB     已经被使用的虚拟内存大小
38VmallocChunk:   34359728764 kB
39HardwareCorrupted:     0 kB
40AnonHugePages:    198656 kB
41HugePages_Total:       0
42HugePages_Free:        0
43HugePages_Rsvd:        0
44HugePages_Surp:        0
45Hugepagesize:       2048 kB
46DirectMap4k:        7168 kB
47DirectMap2M:     1044480 kB

可以看到上面的内存具体用途显示的特别详细,有点像饭店或超市的帐单。其中有几个我们经常接触到的有buffer 、cached 、slab、hugepage、vmall 。前两者就不说了,slab后面也会单独提到,hugepage是为了加快IO处理的速度设置的一个调优项,在数据库、虚拟化上经常会调节该部分,也会出现该部分占用较大的情况。vmall 是一个些许专业的概念,具体可以参看百科csdn上的介绍。该值我们可以在内核启动时vmalloc=xxx来指定。由于这部分显示的要远大于物理内存,平时我们时关注VmallocUsed一项即可。

所以针对上面提到的进程本身占用都不多,而free -m 确发现已用几十G的情况,就可以在这里查看到具体是那一项占的高,很可能是hugepage或slab 。

2、nmon

/proc/meminfo显示的结果非常详细也非常直观,不过我们平时关心的主要就那几个,而且显示如此多,很多一些重要的东西就忽略掉了,这里可以借助IBM出的工具nmon来查看内存分配情况。如下图:

IBM nmon memory
IBM nmon memory

从上面这个输出,也可以看到主要内存还是被active占用的。其次是cached和buffers 。其他几块并没有占用多少内存。

注意:SecureCRT下nmon会有乱码出现,通过将终端类型修改为vt100并重连就OK了。

3、slabtop

内核的模块在分配资源的时候,为了提高效率和资源的利用率,都是透过slab来分配的。slab为结构性缓存占用内存,该项也经常占用很大的内存。不过借助slabtop工具,我们可以很方便的显示内核片缓存信息,该工具可以更直观的显示/proc/slabinfo下的内容。理解意思就行,如下图:

slabtop memory
slabtop memory

如上图所示,objs是指对象的个数,obj_size为对象的大小,use是使用的百分比,这个百分比是针对后面的cache_size来说的。cache_size是给kmem_cache所有对象分配的总大小。我们可以将slabtop查看到的结果与/proc/slabinfo的结果做一个对比,如下,其中/proc/slabinfo中的第三列与第四列的值为num_objs和objsize。这里通过slabtop取得的cache_size乘百分比得到的结果和/proc/slabinfo 3、4列乘积求和得到的结果基本相同。

1[root@361way 01]# slabtop -o|column  -t|grep ^[0-9]|awk 'BEGIN{sum=0;}{sum=sum+int($(NF-1)*int($3));}END{print sum/1024/100}'
249.4671
3[root@361way 01]# cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'
451.9877

同样,在对slabtop不乘百分比时求和与/proc/meminfo里slab项得到的值也基本相同:

1[root@361way 01]# slabtop -o|column  -t|grep ^[0-9]|awk 'BEGIN{sum=0;}{sum=sum+int($(NF-1));}END{print sum/1024}'
254.0898

所以我们也可以通过/proc/slabinfo获取占用内存大于100M的对象集:

1# cat /proc/slabinfo |awk '{if($3*$4/1024/1024 > 100){print $1,$3*$4/1024/1024} }'
2 ext3_inode_cache 282.575
3 proc_inode_cache 2154.03
4 dentry_cache 868.075

如上面的结果,可以看到发现proc_inode这个占了2G多。是占比较多的。

三、进程内存及其他

和内存相关的虚拟文件还有如下这些,其中我们比较重点关注的是smaps和status文件,smem工具就是就是根据这些文件进行的统计分析与处理,具体可以参看我的另一篇博文---linux用smem分析内存占用情况

1/proc/$pid/cmdline
2/proc/$pid/smaps   详细的内存占用情况
3/proc/$pid/status   详细的内存占用情况
4/proc/$pid/maps
5/proc/$pid/stat
6/proc/meminfo

这里我们以hhvm 程序的内存为例,这两个文件的内容如下:

 1[root@361way 01]# cat /proc/22186/smaps |more     //内存使用的分类明细
 200400000-02335000 r-xp 00000000 ca:01 1073823                            /usr/bin/hhvm
 3Size:              31956 kB
 4Rss:               30496 kB
 5Pss:               30494 kB
 6Shared_Clean:          4 kB
 7Shared_Dirty:          0 kB
 8Private_Clean:     30492 kB
 9Private_Dirty:         0 kB
10Referenced:         8136 kB
11Anonymous:             0 kB
12AnonHugePages:         0 kB
13Swap:                  0 kB
14KernelPageSize:        4 kB
15MMUPageSize:           4 kB
1602534000-025b8000 rwxp 01f34000 ca:01 1073823                            /usr/bin/hhvm
17Size:                528 kB
18Rss:                 452 kB
19Pss:                 450 kB
20Shared_Clean:          4 kB
21Shared_Dirty:          0 kB
22Private_Clean:       364 kB
23Private_Dirty:        84 kB
24Referenced:          100 kB
25Anonymous:            84 kB
26AnonHugePages:         0 kB
27Swap:                  0 kB
28KernelPageSize:        4 kB
29MMUPageSize:           4 kB
30025b8000-02603000 rwxp 00000000 00:00 0
31Size:                300 kB
32Rss:                 252 kB
33Pss:                 252 kB
34Shared_Clean:          0 kB
35Shared_Dirty:          0 kB
36Private_Clean:         0 kB
37Private_Dirty:       252 kB
38Referenced:          164 kB
39Anonymous:           252 kB
40AnonHugePages:         0 kB
41Swap:                  0 kB
42KernelPageSize:        4 kB
43MMUPageSize:           4 kB
44029b7000-02c39000 rwxp 01fb7000 ca:01 1073823                            /usr/bin/hhvm
45Size:               2568 kB
46Rss:                   8 kB
47Pss:                   8 kB
48[root@361way 01]# cat /proc/22186/status      //本进程的内存使用概述
49Name:   hhvm
50State:  S (sleeping)
51Tgid:   22186
52Pid:    22186
53PPid:   1
54TracerPid:      0
55Uid:    500     500     500     500
56Gid:    500     500     500     500
57Utrace: 0
58FDSize: 64
59Groups: 500
60VmPeak:  1198068 kB
61VmSize:  1198064 kB
62VmLck:         0 kB
63VmHWM:    369216 kB
64VmRSS:    318504 kB
65VmData:   772992 kB
66VmStk:        88 kB
67VmExe:     31956 kB
68VmLib:    144840 kB
69VmPTE:      1688 kB
70VmSwap:        0 kB
71Threads:        4
72SigQ:   2/7789
73SigPnd: 0000000000000000
74ShdPnd: 0000000000000000
75SigBlk: 0000000000000000
76SigIgn: 0000000000001000
77SigCgt: 0000000182006eed
78CapInh: 0000000000000000
79CapPrm: 0000000001800400
80CapEff: 0000000001800400
81CapBnd: ffffffffffffffff
82Cpus_allowed:   7fff
83Cpus_allowed_list:      0-14
84Mems_allowed:   00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
85Mems_allowed_list:      0
86voluntary_ctxt_switches:        492
87nonvoluntary_ctxt_switches:     194

常见的内存分析手段和工具基本上就上面这些,如果想和更深一步的了解内存在kernel 中的分配原理,可以参看如下内存管理页面:

Linux Memory Management

Memory Management

Linux 内存管理