Home > Linux, 源码分析 > Linux Used内存到底哪里去了?

Linux Used内存到底哪里去了?

January 19th, 2013

原创文章,转载请注明: 转载自系统技术非业余研究

本文链接地址: Linux Used内存到底哪里去了?

前几天 纯上 同学问了一个问题:

我ps aux看到的RSS内存只有不到30M,但是free看到内存却已经使用了7,8G了,已经开始swap了,请问ps aux的实际物理内存统计是不是漏了哪些内存没算?我有什么办法确定free中used的内存都去哪儿了呢?

这个问题不止一个同学遇到过了,之前子嘉同学也遇到这个问题,内存的计算总是一个迷糊账。 我们今天来把它算个清楚下!

通常我们是这样看内存的剩余情况的:

$free -m
             total       used       free     shared    buffers     cached
Mem:         48262       7913      40349          0         14        267
-/+ buffers/cache:       7631      40631
Swap:         2047        336       1711

那么这个信息是如何解读的呢,以下这个图解释的挺清楚的!
free

补充(不少人反映图不清晰,请参考:http://www.redbooks.ibm.com/redpapers/pdfs/redp4285.pdf P46-47)

上面的情况下我们总的内存有48262M,用掉了7913M。 其中buffer+cache总共14+267=281M, 由于这种类型的内存是可以回收的,虽然我们用掉了7913M,但是实际上我们如果实在需要的话,这部分buffer/cache内存是可以放出来的。

我们来演示下:

$ sudo sysctl vm.drop_caches=3
vm.drop_caches = 3
$ free -m
             total       used       free     shared    buffers     cached
Mem:         48262       7676      40586          0          3         41
-/+ buffers/cache:       7631      40631
Swap:         2047        336       1711

我们把buffer/cache大部分都清除干净了,只用了44M,所以我们这次used的空间是7676M。
到现在我们比较清楚几个概念:
1. 总的内存多少
2. buffer/cache内存可以释放的。
3. used的内存的概率。

即使是这样我们还是要继续追查下used的空间(7637M)到底用到哪里去了?

这里首先我们来介绍下nmon这个工具,它对内存的使用显示比较直观。
nmon

使用的内存的去向我们很自然的就想到操作系统系统上的各种进程需要消耗各种内存,我们透过top工具来看下:
top

通常我们会看进程的RES这一项,这项到底是什么意思呢?这个数字从哪里出来的呢? 通过strace对top和nmon的追踪和结合源码,我们确定这个值是从/proc/PID/statm的第二个字段读取出来的.
那这个字段什么意思呢?
man proc或者http://www.kernel.org/doc/man-pages/online/pages/man5/proc.5.html 会详细的解释/proc/下的文件的具体意思,我们摘抄下:

/proc/[pid]/statm
Provides information about memory usage, measured in pages. The
columns are:

size total program size
(same as VmSize in /proc/[pid]/status)
resident resident set size
(same as VmRSS in /proc/[pid]/status)
share shared pages (from shared mappings)
text text (code)
lib library (unused in Linux 2.6)
data data + stack
dt dirty pages (unused in Linux 2.6)

resident set size 也就是每个进程用了具体的多少页的内存。由于linux系统采用的是虚拟内存,进程的代码,库,堆和栈使用的内存都会消耗内存,但是申请出来的内存,只要没真正touch过,是不算的,因为没有真正为之分配物理页面。

我们实际进程使用的物理页面应该用resident set size来算的,遍历所有的进程,就可以知道所有的所有的进程使用的内存。
我们来实验下RSS的使用情况:

$ cat 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"
$ ./RSS.sh  
7024692KB

从数字来看,我们的进程使用了大概7024M内存,距离7637M还有几百M内存哪里去了? 哪里去了? 猫吃掉了?
我们再回头来仔细看下nmon的内存统计表。

nmon1

那个该死的slab是什么呢? 那个PageTables又是什么呢?

简单的说内核为了高性能每个需要重复使用的对象都会有个池,这个slab池会cache大量常用的对象,所以会消耗大量的内存。运行命令:

$ slabtop

我们可以看到:
slabtop
从图我们可以看出各种对象的大小和数目,遗憾的是没有告诉我们slab消耗了多少内存。
我们自己来算下好了:

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

好吧,把每个对象的数目*大小,再累加,我们就得到了总的内存消耗量:904M

那么PageTables呢? 我们万能的内核组的同学现身了:

伯瑜:
你还没有计算page tables的大小,还有struct page也有一定的大小(每个页一个,64bytes),如果是2.6.32的话,每个页还有一个page_cgroup(32bytes),也就是说内存大小的2.3%(96/4096)会被内核固定使用的
含黛:
struct page是系统boot的时候就会根据内存大小算出来分配出去的,18内核是1.56%左右,32内核由于cgroup的原因会在2.3%

好吧,知道是干嘛的啦,管理这些物理页面的硬开销,那么具体是多少呢?

$ echo `grep PageTables /proc/meminfo | awk '{print $2}'` KB
58052 KB

好吧,小结下!内存的去向主要有3个:1. 进程消耗。 2. slab消耗 3.pagetable消耗。
我把三种消耗汇总下和free出的结果比对下,这个脚本的各种计算项仲同学帮忙搞定的:

$ cat cm.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`
PageTable=`grep PageTables /proc/meminfo | awk '{print $2}'`
SlabInfo=`cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'`

echo $RSS"KB", $PageTable"KB", $SlabInfo"MB"
printf "rss+pagetable+slabinfo=%sMB\n" `echo $RSS/1024 + $PageTable/1024 + $SlabInfo|bc`
free -m

$ ./cm.sh
7003756KB, 59272KB, 904.334MB
rss+pagetable+slabinfo=7800.334MB
             total       used       free     shared    buffers     cached
Mem:         48262       8050      40211          0         17        404
-/+ buffers/cache:       7629      40633
Swap:         2047        336       1711

free报告说7629M, 我们的cm脚本报告说7800.3M, 我们的CM多报了171M。
damn,这又怎么回事呢?

我们重新校对下我们的计算。 我们和nmon来比对下,slab和pagetable的值是吻合的。 那最大的问题可能在进程的消耗计算上。
resident resident set size 包括我们使用的各种库和so等共享的模块,在前面的计算中我们重复计算了。

$ pmap `pgrep bash`
...
22923:   -bash
0000000000400000    848K r-x--  /bin/bash
00000000006d3000     40K rw---  /bin/bash
00000000006dd000     20K rw---    [ anon ]
00000000008dc000     36K rw---  /bin/bash
00000000013c8000    592K rw---    [ anon ]
000000335c400000    116K r-x--  /lib64/libtinfo.so.5.7
...
0000003ec5220000      4K rw---  /lib64/ld-2.12.so
0000003ec5221000      4K rw---    [ anon ]
0000003ec5800000   1628K r-x--  /lib64/libc-2.12.so
...
0000003ec5b9c000     20K rw---    [ anon ]
00007f331b910000  96836K r----  /usr/lib/locale/locale-archive
00007f33217a1000     48K r-x--  /lib64/libnss_files-2.12.so
...
00007f33219af000     12K rw---    [ anon ]
00007f33219bf000      8K rw---    [ anon ]
00007f33219c1000     28K r--s-  /usr/lib64/gconv/gconv-modules.cache
00007f33219c8000      4K rw---    [ anon ]
00007fff5e553000     84K rw---    [ stack ]
00007fff5e5e4000      4K r-x--    [ anon ]
ffffffffff600000      4K r-x--    [ anon ]
 total           108720K

多出的171M正是共享库重复计算的部分。
但是由于每个进程共享的东西都不一样,我们也没法知道每个进程是如何共享的,没法做到准确的区分。

所以只能留点小遗憾,欢迎大家来探讨。

总结:内存方面的概念很多,需要深入挖掘!

祝玩的开心!

Post Footer automatically generated by wp-posturl plugin for wordpress.

Categories: Linux, 源码分析 Tags: , , , ,
  1. zjj.tq
    January 19th, 2013 at 18:28 | #1

    沙发

  2. January 19th, 2013 at 20:25 | #2

    不熟悉slab和pagetables的童鞋,表示很受教 .

  3. xkglob刀
    January 19th, 2013 at 20:25 | #3

    # ./cm.sh
    3173388KB, 41484KB, 78.8269MB
    rss+pagetable+slabinfo=3217.8269MB
    total used free shared buffers cached
    Mem: 3851 3811 40 0 46 2446
    -/+ buffers/cache: 1318 2533
    Swap: 9773 48 9725

    我在一个起了两个oracle数据库的机器上的结果,所以cm.sh也会重复计算shared memory的部分.

  4. haofish
    January 19th, 2013 at 21:54 | #4

    关于重复计算的部分,还有可能是共享内存占用了,这种可以在/proc/$pid/smaps 里面可以看到(匿名的那些)

    Yu Feng Reply:

    多谢指点!

    homer Reply:

    多谢,我一直没搞明白 ,smaps里面没名字的是啥东东。

    Yu Feng Reply:

    这块应该没法算。

  5. January 19th, 2013 at 23:15 | #5

    好文。。。霸爷辛苦了。

  6. 吴洪辉
    January 19th, 2013 at 23:52 | #6

    @haofish

    冒泡一下,完全正确,smap里面这些信息可以提供进程使用的共享库的内存页面信息。不过要精确统计还是略困难的。。。再说脚本运行也有开开关关进程的,有影响。

    Rss: 4 kB
    Pss: 4 kB
    Shared_Clean: 0 kB
    Shared_Dirty: 0 kB
    Private_Clean: 0 kB
    Private_Dirty: 4 kB

    Yu Feng Reply:

    对的。

  7. January 20th, 2013 at 11:02 | #7

    好文章,赞

    计算RSS大小的那个脚本,耗时比较久,用awk一行就应该可以了:

    awk ‘$2 > 0 {sum+=$2} END {printf “%d\n”, sum*4/1024}’ /proc/[0-9]*/statm

    Yu Feng Reply:

    要检查线程是否有statm, 内核线程没有的。

  8. zhp_johnny
    January 20th, 2013 at 14:47 | #8

    请教一下,文章中提到:

    “所以我们这次used的空间是7676M。”

    而5行之后又提到:
    “继续追查下used的空间(7637M)到底用到哪里去了?”

    中间差的 39M 内存, 是怎么算出来的?

    Yu Feng Reply:

    不是清空buffer & cache省出来的吗?

    zhp_johnny Reply:

    多谢。 从文章中看, 去掉buffer & cache后,used剩余是 7631M,是我理解错了么? 请指正,谢谢。

    如下,其中 used是7631:

    $ free -m
    total used free shared buffers cached
    Mem: 48262 7676 40586 0 3 41
    -/+ buffers/cache: 7631 40631
    Swap: 2047 336 1711

    Yu Feng Reply:

    这里的used的意思是:物理页被占用,无法释放出来。因为我们清过buffer和cache,有(3+41=44)M无法释放,所以就算在里面了。

  9. January 21st, 2013 at 11:03 | #9

    ddb@db-xx:~/jhh$ sudo ./cm.sh

    30497460KB, 151192KB, 166.066MB
    rss+pagetable+slabinfo=30095.066MB
    total used free shared buffers cached
    Mem: 48394 36336 12058 0 6 10632
    -/+ buffers/cache: 25696 22698
    Swap: 2047 1741 305

    这里算出来是30095 , 实际 25696 , 差别还是满大的。 内存就是一笔糊涂账

    Yu Feng Reply:

    这块共享的如果单单是代码段,大概也就固定的100多M,可以ignore. 如果是共享内存什么的,那还是不好折腾。但是你至少知道有这回事!

  10. kty
    January 27th, 2013 at 09:02 | #10

    内核除了slab可能还有其他占用的内存?比如内核代码段?

    Yu Feng Reply:

    恩,但是这块的代码的占用基本上是死的,不会变化。

  11. quixote
    February 9th, 2013 at 16:11 | #11

    关于共享库的重复计算
    /proc/pid/smaps里面不是有Pss一项么?
    这个是 共享内存/共享数 后的均值
    比如
    A: 10M + 10M .so
    B: 5M + 10M.so

    总共 25M
    Rss 算出来是 A 20M, B 15M
    Pss 算出来是 A 15M, B 10M

    共享的那10M 被除以2(共享数了)

    参见
    http://stackoverflow.com/questions/9922928/what-does-pss-mean-in-proc-pid-smaps

  12. huanzi
    February 19th, 2013 at 09:34 | #12

    最上边那个图可以这样理解么?
    14+267+7631 = 7913?

    Yu Feng Reply:

    是这么理解的。

  13. dmee
    March 7th, 2013 at 12:14 | #13

    想请教下霸爷,关于服务器程序中oom的问题,有啥抓虫经验或者资料,希望能给小弟一点指点。谢谢

  14. dmee
    March 7th, 2013 at 12:15 | #14

    想请教下霸爷,关于服务器程序中oom的问题,有啥抓虫经验或者资料,希望能给小弟一点指点。

    Yu Feng Reply:

    OOM的原因比较多,我们有些积累,你想具体了解那方面的东西?

    dmee Reply:

    我怀疑是程序有内存泄露,但是不知道该怎么查。之前用valgrind跑过,由于我们程序里有用到内存池,
    valgrind有太多误报,没什么有用的信息。。。

  15. 9sheng
    March 28th, 2013 at 16:21 | #15

    [root@vm-vmw4133-t ~]# ./cm.sh
    128164KB, 7808KB, 37.1054MB
    rss+pagetable+slabinfo=169.1054MB
    total used free shared buffers cached
    Mem: 16051 10664 5387 0 8 97
    -/+ buffers/cache: 10558 5493
    Swap: 6719 0 6719
    [root@vm-vmw4133-t ~]# uname -a
    Linux vm-vmw4133-t 2.6.18-128.el5 #1 SMP Wed Dec 17 11:41:38 EST 2008 x86_64 x86_64 x86_64 GNU/Linux
    服务器上执行脚本的结果,free 显示用了10G,rss+pagetable+slabinfo=169.1054MB。有什么问题么?

  16. peixinchen
    April 1st, 2013 at 11:11 | #16

    @9sheng
    和我的情况很相似,free显示用了12G,但脚本跑出来只有500M,差距很大

    93844KB, 1736KB, 381.187MB
    rss+pagetable+slabinfo=473.187MB
    total used free shared buffers cached
    Mem: 16024 14422 1602 0 547 1505
    -/+ buffers/cache: 12369 3655
    Swap: 2054 0 2054

    —— Shared Memory Segments ——–
    key shmid owner perms bytes nattch status
    0x00005feb 0 root 666 12000 3
    0x00005fe7 32769 root 666 524288 2
    0x00005fe8 65538 root 666 2097152 1
    0x00016512 131075 root 644 32 3
    0x00016511 163844 root 644 136222 1
    0x00035355 3866629 root 666 11233604 3

    BJ-TJ-172-18-24-213:~/nmon # ./calcmem.sh
    93988KB, 1728KB, 381.153MB
    rss+pagetable+slabinfo=473.153MB
    total used free shared buffers cached
    Mem: 16024 14424 1599 0 547 1507
    -/+ buffers/cache: 12369 3654
    Swap: 2054 0 2054

    —— Shared Memory Segments ——–
    key shmid owner perms bytes nattch status
    0x00005feb 0 root 666 12000 3
    0x00005fe7 32769 root 666 524288 2
    0x00005fe8 65538 root 666 2097152 1
    0x00016512 131075 root 644 32 3
    0x00016511 163844 root 644 136222 1
    0x00035355 3866629 root 666 11233604 3

  17. holybless
    April 3rd, 2013 at 01:23 | #17

    请教霸爷
    sh cm.sh
    6239676KB, 30676KB, 86.9185MB
    rss+pagetable+slabinfo=6208.9185MB
    total used free shared buffers cached
    Mem: 24097 22699 1397 0 59 1424
    -/+ buffers/cache: 21215 2882
    Swap: 8189 2294 5894

    不明白为什么used是21g,而我们计算出来的只有不到6g,求教

    请叫我雷锋 Reply:

    帖子中的计算公式是有限定场景的,比如没有考虑到配置HugePages的情况,请查看cat /proc/meminfo中的统计情况是否有配置大页内存,这些内存会被计算到used中,但是一般的进程不可见,自然也不会在RSS、slab和pagetable的开销中了。举例如下:
    HugePages_Total: 153650
    HugePages_Free: 153650
    HugePages_Rsvd: 0
    HugePages_Surp: 0
    Hugepagesize: 2048 kB
    配置了153650个大页,单页大小2M,约300G内存都是空闲,但是会被统计到used中。
    @holybless

  18. hbmao
    June 17th, 2013 at 15:59 | #18

    请教问题呀,我用free每隔5分钟监控一下系统。
    free逐渐减小,cached逐渐增加,但是free+cached+buffer的总和却在减小。
    用ps aux和到/proc/pid/status中查看每隔进程的内存数,发现没有变化。
    内核slab有增加,但是却没有系统可用内存下降的那么多。
    求解释呀,已经搞了一个星期无法解决了。

  19. Daemon
    July 9th, 2013 at 14:19 | #19

    @zhp_johnny
    用K为单位算一下没问题,用M的话有损失

  20. bakey
    July 13th, 2013 at 01:27 | #20

    RSS.sh 算出来的值有点问题吧?
    这样算出来的单位是byte,不是KB

  21. huahua
    November 12th, 2013 at 10:20 | #21

    你好,我在使用mongodb的过程中,通过free 看到第二行实际的使用才5g内存,
    但是通过top查看res和使用ps aux|grep mongodb的命令查看已经使用了108g内存(机器是256g)

    mongodb有采用mmap的映射,关于mmap的内存是怎么统计的了,这部分使用的内存是不是可以回收的了

    Yu Feng Reply:

    mmap是公用的内存,按照这种算法,会重复计算。

  22. Aman-zhai
    January 27th, 2014 at 10:46 | #22

    linux kernel中的module动态分配的内存(并非从 slab中获取)在什么地方计算的?

    Yu Feng Reply:

    这块目前还没有太好的思路。

  23. Aman-zhai
    January 27th, 2014 at 10:53 | #23

    而且使用脚本获取statm的时候,可能会有不准的情况,在计算的同事,进程数以及内存在不断的变化,获取的 RSS还要去除共用lib的部分;我使用获取smaps的PSS的方法同样计算出来会有出入感觉不是太准。 我使用dumpsys meminfo获取了total的PSS 加上 free + buffer + cache,再加上pagetable以及page页得内存后仍然有十几M的内存无法获知具体的去向,我想这个只有自己加上些log才能获取了

    Yu Feng Reply:

    10M太微小了,基本可以ignore.

  24. xiaoliu
    April 26th, 2015 at 23:22 | #24

    sh cm.sh
    31596KB, 0KB, 17.5447MB
    rss+pagetable+slabinfo=47.5447MB
    total used free shared buffers cached
    Mem: 8204 7320 884 0 14 70
    -/+ buffers/cache: 7235 969
    Swap: 4094 0 4094
    请问这个是啥情况?完全不知道buffers的7235哪来的?

  25. leeyou
    May 13th, 2015 at 12:18 | #25

    pagetables应该是页表(pte)的消耗吧?不是struct page的

  26. andyhui
    June 26th, 2015 at 12:44 | #26

    @leeyou
    恩,我也觉得pagetables是指页表,不是struct page. 其实struct page占的内存相对与整个内存来说不大。4GB的物理内存,struct page只占20M。

  27. yuanxg
    June 13th, 2016 at 10:57 | #27

    请教霸爷,除了rss、pagetable、slabinfo、HugePages外内存占用的情况还有哪些,有什么工具可以查看出来。

Comments are closed.