深入分析linux内存使用的情况详解

前两天一台128G内存的oracle主机发生故障触发kdump,最终由于var目录空间不足,导致kdump生成不完全。结合之前redhat给出的建议,crash设置的空间最好大于memory 空间。对此我们做了一个简单的计算,认为kdump主机生成的是运行在内存里的信息 ,虽然主机有128G的内存,不过通过top查看并计算后发现我实际上只使用7G多的大小,而使用free -m查看时已经使用了80G左右的内存 。站在DBA的角度看的话,这部分内存提前分配给了sga ,貌似也可以讲通。记得之前看过taobao褚霸写的一篇分析。这里再结合该文章算算。

通过褚霸的Linux Used内存到底哪里去了?我们已经了解到内存主要消耗在三个方面:1. 进程消耗。 2. slab消耗 3.pagetable消耗。

由于不便于直接在现网oracle主机上进行操作,这里就以本blog的云主机为例进行测试。

一、查看已用内存总量

[root@91it ~]# free -m
             total       used       free     shared    buffers     cached
Mem:           996        908         88          0        174        283
-/+ buffers/cache:        450        546
Swap:            0          0          0

于已用内存和可用内存这已经是一个老生长谈的问题了。这里看到的信息如下:

1、总内存996M ,已用内存908M

2、由于buffers + cached内存实际上也是可用内存,该内存也可以通过echo 3 > /proc/sys/vm/drop_caches 回收pagechae、dentries and inodes ,所以实际上已经使用的内存是450M 。

注:

1、关于内存的计算方法就不上图了,这点可以参考:http://www.redbooks.ibm.com/redpapers/pdfs/redp4285.pdf

2、linux内存强制回收的方法具体可以参考:linux 下 强制回收内存 。

即然实际使用了450M内存,那这450M内存是如何分配的呢?

二、RSS内存(Resident size)

ps下命令下的RSS内存、top工具下的RES内存都是指的这一块内存。resident set size 也就是每个进程用了具体的多少页的内存。由于linux系统采用的是虚拟内存,进程的代码,库,堆和栈使用的内存都会消耗内存,但是申请出来的内存,只要没真正touch过,是不算的,因为没有真正为之分配物理页面,说白了也就是真正具有“数据页的物理内存”。我这里使用的是一段从python psutil 模块里演化出的一段代码进行计算的:

[root@91it ~]# cat vms.py
#!/usr/bin/python
import os
def get_vms(pid):
            with open("/proc/%s/statm" % pid, "rb") as f:
                vms = f.readline().split()[1]
                return int(vms)
pids = [int(x) for x in os.listdir(b'/proc') if x.isdigit()]
vmss = [get_vms(pid) for pid in pids]
print sum(vmss) * 4
[root@91it ~]# python vms.py
386528
注:

1、/proc/PID/statm 第二列是RSS内存使用page页的多少,而在linux下默认使用的page页大小是4KB 。所以我上面计算求和后,最后乘以4 ,而我最终计算出的结果就是386528KB 。

2、这里也可以通过/proc/PID/status里的vmRss项进行求和,因为该项直接给出的是KB值。

[root@91it ~]# cat /proc/998/status
Name:   mingetty
State:  S (sleeping)
Tgid:   998
Pid:    998
PPid:   1
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0
Utrace: 0
FDSize: 64
Groups:
VmPeak:     4068 kB
VmSize:     4064 kB
VmLck:         0 kB
VmHWM:       556 kB
VmRSS:        76 kB
………………………………
当然也可以使用shell 进行计算:

$ 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内存部分,具体可以查看man proc手册或kernl 页的介绍 ,以下是man 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)

二、slab内存

slab内存的作用是内核为了高性能每个需要重复使用的对象都会有个池,这个slab池会cache大量常用的对象,所以会消耗大量的内存。具体可以运行slabtop命令查看

slab内存的消耗我们可以通过/proc/slabinfo文件算出,具脚本为:

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

三星、PageTables内存

这部分内存我并没有细研究,这里就直接拉taobao上各位大牛的说法用吧:“struct page也有一定的大小(每个页一个,64bytes),如果是2.6.32的话,每个页还有一个page_cgroup(32bytes),也就是说内存大小的2.3%(96/4096)会被内核固定使用 。struct page是系统boot的时候就会根据内存大小算出来分配出去的,18内核是1.56%左右,32内核由于cgroup的原因会在2.3% 。”

而具体消耗可以通过/proc/meminfo里的pagetables项获取,脚本如下:

# echo `grep PageTables /proc/meminfo | awk '{print $2}'` KB
4476 KB
系统的硬开销占比并不多。

四、算算总帐

三者加起来发现要大于450M 。这里我们便于查看,再跑下脚本:

$ 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=%sMBn" `echo $RSS/1024 + $PageTable/1024 + $SlabInfo|bc`
free -m
$ ./cm.sh
382048KB, 4528KB, 74.8561MB
rss+pagetable+slabinfo=451.8561MB
             total       used       free     shared    buffers     cached
Mem:           996        842        154          0        106        376
-/+ buffers/cache:        359        637
Swap:            0          0          0
由于上面演示时,我强制回收过内存。目前实际已用内存为359M ,而我们上面三者之和是451M,比实际使用的大了100M左右。

多出的这部分内存是我们在计算rss内存时算重复了。因为rss内存包括我们使用的各种库和so等共享的模块。具体可以使用pmap指令查看详细的每个进程调用的lib库及已用内存值。这里以最简单的bash进程为例。

[root@91it ~]# pmap `pgrep bash`
1464:   -bash
0000000000400000    848K r-x--  /bin/bash
00000000006d3000     40K rw---  /bin/bash
00000000006dd000     20K rw---    [ anon ]
00000000008dc000     36K rw---  /bin/bash
00000000011a4000    396K rw---    [ anon ]
0000003ef9800000    128K r-x--  /lib64/ld-2.12.so
0000003ef9a1f000      4K r----  /lib64/ld-2.12.so
0000003ef9a20000      4K rw---  /lib64/ld-2.12.so
0000003ef9a21000      4K rw---    [ anon ]
0000003ef9c00000   1576K r-x--  /lib64/libc-2.12.so
0000003ef9d8a000   2048K -----  /lib64/libc-2.12.so
0000003ef9f8a000     16K r----  /lib64/libc-2.12.so
0000003ef9f8e000      4K rw---  /lib64/libc-2.12.so
0000003ef9f8f000     20K rw---    [ anon ]
0000003efa400000      8K r-x--  /lib64/libdl-2.12.so
0000003efa402000   2048K -----  /lib64/libdl-2.12.so
0000003efa602000      4K r----  /lib64/libdl-2.12.so
0000003efa603000      4K rw---  /lib64/libdl-2.12.so
0000003efb800000    116K r-x--  /lib64/libtinfo.so.5.7
0000003efb81d000   2048K -----  /lib64/libtinfo.so.5.7
0000003efba1d000     16K rw---  /lib64/libtinfo.so.5.7
00007f1a42fc8000  96836K r----  /usr/lib/locale/locale-archive
00007f1a48e59000     48K r-x--  /lib64/libnss_files-2.12.so
00007f1a48e65000   2048K -----  /lib64/libnss_files-2.12.so
00007f1a49065000      4K r----  /lib64/libnss_files-2.12.so
00007f1a49066000      4K rw---  /lib64/libnss_files-2.12.so
00007f1a49067000     12K rw---    [ anon ]
00007f1a4906b000      8K rw---    [ anon ]
00007f1a4906d000     28K r--s-  /usr/lib64/gconv/gconv-modules.cache
00007f1a49074000      4K rw---    [ anon ]
00007fff48189000     84K rw---    [ stack ]
00007fff481ff000      4K r-x--    [ anon ]
ffffffffff600000      4K r-x--    [ anon ]
 total           108472K
2757:   -bash
0000000000400000    848K r-x--  /bin/bash
00000000006d3000     40K rw---  /bin/bash
00000000006dd000     20K rw---    [ anon ]
00000000008dc000     36K rw---  /bin/bash
0000000001385000    396K rw---    [ anon ]
0000003ef9800000    128K r-x--  /lib64/ld-2.12.so
0000003ef9a1f000      4K r----  /lib64/ld-2.12.so
0000003ef9a20000      4K rw---  /lib64/ld-2.12.so
0000003ef9a21000      4K rw---    [ anon ]
0000003ef9c00000   1576K r-x--  /lib64/libc-2.12.so
0000003ef9d8a000   2048K -----  /lib64/libc-2.12.so
0000003ef9f8a000     16K r----  /lib64/libc-2.12.so
0000003ef9f8e000      4K rw---  /lib64/libc-2.12.so
0000003ef9f8f000     20K rw---    [ anon ]
0000003efa400000      8K r-x--  /lib64/libdl-2.12.so
0000003efa402000   2048K -----  /lib64/libdl-2.12.so
0000003efa602000      4K r----  /lib64/libdl-2.12.so
0000003efa603000      4K rw---  /lib64/libdl-2.12.so
0000003efb800000    116K r-x--  /lib64/libtinfo.so.5.7
0000003efb81d000   2048K -----  /lib64/libtinfo.so.5.7
0000003efba1d000     16K rw---  /lib64/libtinfo.so.5.7
00007fda04cb1000  96836K r----  /usr/lib/locale/locale-archive
00007fda0ab42000     48K r-x--  /lib64/libnss_files-2.12.so
00007fda0ab4e000   2048K -----  /lib64/libnss_files-2.12.so
00007fda0ad4e000      4K r----  /lib64/libnss_files-2.12.so
00007fda0ad4f000      4K rw---  /lib64/libnss_files-2.12.so
00007fda0ad50000     12K rw---    [ anon ]
00007fda0ad54000      8K rw---    [ anon ]
00007fda0ad56000     28K r--s-  /usr/lib64/gconv/gconv-modules.cache
00007fda0ad5d000      4K rw---    [ anon ]
00007fff0e9e0000     84K rw---    [ stack ]
00007fff0e9ff000      4K r-x--    [ anon ]
ffffffffff600000      4K r-x--    [ anon ]
 total           108472K
从上面的指令上看出,pid为1464和2757两个进程使用很多相同的so文件,而且其内存地址也相同。这里个人认为rss部分的内存理论上是可以完全计算准确的。做法就是先遍历/proc下所有的pid,再pmap所有pid,对所有的输出结果进行汇总后去重。再将第二列的占用内存值求和(具体也可以考虑从/proc/pid/smaps文件入手 )。

五、其他情况

在第四步算算总帐中,我们看出 rrs内存 + slab内存 + pagetables内存 > 实际已用内存。但该情况并非是绝对的,也有例外。在我上面提到的oracle 使用场景下,事先分配了70g左右的oracle sga 空间(即内存空间)。而三者相加要远远小于实际使用的物理内存。这又要怎么解释呢?再去除cache和buffer的空间也要远远小于实际使用的内存。

[root@irora04s ~]# free -m
             total       used       free     shared    buffers     cached
Mem:        129023     100024      28999          0        885      12595
-/+ buffers/cache:      86543      42480
Swap:        24575          0      24575
[root@irora04s ~]# sh /tmp/mem.sh
4339696KB, 66056KB, 745.805MB
rss+pagetable+slabinfo=5046.805MB
             total       used       free     shared    buffers     cached
Mem:        129023     100096      28926          0        885      12597
-/+ buffers/cache:      86614      42409
Swap:        24575          0      24575
我个人的理解是事先分配的这部分sga内存,大部分是空page页,在未使用时虽然空间被占用了,但该内存地址内并不存在数据。所以一旦该机触发kdump 时,crash 的内存空间占用的磁盘空间 接近于rss+pagetable+slabinfo ,小于rss+pagetable+slabinfo+buffers+cached 。

最后这里同样推荐有时间看下nmon的源码。因为从nmon的内存统计信息来看,更便于理解内存的几个去向:

时间: 2024-10-23 01:02:19

深入分析linux内存使用的情况详解的相关文章

Linux内存管理之mmap详解 【转】

转自:http://blog.chinaunix.net/uid-26669729-id-3077015.html 一. mmap系统调用 1. mmap系统调用         mmap将一个文件或者其它对象映射进内存.文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零.munmap执行相反的操作,删除特定地址区域的对象映射. 当使用mmap映射文件到进程后,就可以直接操作这段虚拟地址进行文件的读写等操作,不必再调用read,write等系统调用.但需注

深入分析Java内存区域的使用详解_java

Java 内存划分:     在Java内存分配中,java将内存分为:方法区,堆,虚拟机栈,本地方法栈,程序计数器.其中方法区和堆对于所有线程共享,而虚拟机栈和本地方法栈还有程序计数器对于线程隔离的.每个区域都有各自的创建和销毁时间. 程序计数器:     作用是当前线程所执行的字节吗的行号指示器.Java的多线程是通过线程轮流切换并分配处理器执行时间方式来实现的.因此,每个线程为了能在切换后能恢复到正确的位置,每个线程需要独立的程序计数器. Java 虚拟机栈:     每个放在被执行的时候

Linux进程上下文切换过程context_switch详解--Linux进程的管理与调度(二十一)【转】

转自:http://blog.csdn.net/gatieme/article/details/51872659 版权声明:本文为博主原创文章 && 转载请著名出处 @ http://blog.csdn.net/gatieme   目录(?)[-] 前景回顾 1 Linux的调度器组成 2 调度工作 进程上下文 1 进程上下文的概念 2 上下文切换 context_switch进程上下文切换 1 context_switch完全注释 2 prepare_arch_switch切换前的准备工

Linux驱动开发必看详解神秘内核(完全转载)

Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html   IT168 技术文档]在开始步入Linux设备驱动程序的神秘世界之前,让我们从驱动程序开发人员的角度看几个内核构成要素,熟悉一些基本的内核概念.我们将学习内核定时器.同步机制以及内存分配方法.不过,我们还是得从头开始这次探索之旅.因此,本章要先浏览一下内核发出的启动信息,然后再逐个讲解一些有意思的点. 2.1 启动过程 图2-1显示

Linux下DNS服务器搭建详解

Linux下DNS服务器搭建详解 简介: DNS服务器的作用就是就好比生活中的电话簿.114查号台一样,为各种网络程序找到对应目标主机的IP地址或对应的主机域名. DNS系统的作用: 正向解析:根据主机名称(域名)查找对应的IP地址(实际应用中最多的) 反向解析:根据IP地址查找对应的主机域名(不常用,一般用于搭建邮件服务器时.) 根据服务器与所提供域名解析记录的关系,将DNS服务器分为不同的角色: 缓存域名服务器 也称为 唯高速缓存服务器 通过向其他域名服务器查询获得域名->IP地址记录 将域

linux 下的yum命令详解_Linux

yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器.基於RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载.安装.yum提供了查找.安装.删除某一个.一组甚至全部软件包的命令,而且命令简洁而又好记. yum的命令形式一般是如下:yum [options] [command] [package ...] 其中的[opt

Linux crontab定时任务配置方法(详解)_Linux

CRONTAB概念/介绍 crontab命令用于设置周期性被执行的指令.该命令从标准输入设备读取指令,并将其存放于"crontab"文件中,以供之后读取和执行. cron 系统调度进程. 可以使用它在每天的非高峰负荷时间段运行作业,或在一周或一月中的不同时段运行.cron是系统主要的调度进程,可以在无需人工干预的情况下运行作业.crontab命令允许用户提交.编辑或删除相应的作业.每一个用户都可以有一个crontab文件来保存调度信息.系统管理员可以通过cron.deny 和 cron

Linux下的chkconfig命令详解_Linux

chkconfig命令 Linux下的chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. 使用语法 chkconfig [–add][–del][–list][系统服务] 或 chkconfig [–level <等级代号>][系统服务][on/off/reset] chkconfig在没有参数运行时,显示用法.如果加上服务名,那么就检查这个服务是否在当前运行级启动.如果是,返回true

linux系统下hosts文件详解及配置_linux shell

hosts文件 hosts -- the static table lookup for host name(主机名查询静态表). hosts文件是Linux系统上一个负责ip地址与域名快速解析的文件,以ascii格式保存在/etc/目录下.hosts文件包含了ip地址与主机名之间的映射,还包括主机的别名.在没有域名解析服务器的情况下,系统上的所有网络程序都通过查询该文件来解析对应于某个主机名的ip地址,否则就需要使用dns服务程序来解决.通过可以将常用的域名和ip地址映射加入到hosts文件中