在服务端编程,对进程的内存占用情况的了解和日常监控显得尤为重要,特别是内存存储密集型进程,比如redis,namenode等,疏忽对内存的分析,肯定会酿成大的线上事故;
###入门篇之free命令解析
free命令可以了解当前操作系统整体可用内存的使用情况,是分析的第一步:
total used free shared buffers cached
Mem: 49406140 48352948 1053192 0 96524 35946452
-/+ buffers/cache: 12309972 37096168
Swap: 0 0 0
在free命令可以看出我们当前物理内存的使用情况,其中total:49406140即为总的物理内存大小,used:48352948为当前被系统调度的内存大小,而free:1053192即当前未被系统调度的内存大小,注意,这里用的词是被系统调度,而不是被系统进程物理占用的内存大小;
怎么理解呢?对于上面的case,free只有100M,如果used理解为被物理占用的内存,那么就只有100M可以留给我新的进程使用?这个理解是不正确的;used只是被系统调度的内存,系统可以从used里面划分出有效的内存来调度给新进程使用,只有在可调度内存不够的情况下,才会从free中挪用新内存进行调度;
used内存会被系统用来做哪些调度?
- 实实在在被进程占用,比如进程申请的栈内存,被分配并被使用的堆内存(这里强调一下分配并被使用,通过new/malloc分配的进程内存空间不一定实实在在占用了物理内存,详情见后面的分析),在进程存活并未主动释放的case下,这块内存是无法被系统调度给其他进程使用,即第三行used=12309972部分;
- 用于写buffer,即上面buffers:96524,对于文件写操作需要经过写buffer的过程再落盘到磁盘上,而buffer这块内存是可以被系统回收并反复被调度的内存;
- 用于读cached,即上面的cached:35946452,系统会将一大部分内存用于cache,比如用于文件预读等,与buffer相同,这部分内存可以被系统重复调度给信息的进程;
因此,一个系统被调度内存大小=buffers+cached+第三行的物理used;可用于新进程的内存大小=free+buffers+cached;而这个数字即为上面第三行free=37096168;第三行表示当前物理内存被系统物理占用的内存,used=12309972,以及可以被调度的内存大小free;而这里free+used即为total物理内存大小;在日常运维过程中,可以通过free命令的第三行的free部分来快速获取机器物理内存被实际占用情况
free,buffer,cached这种值也可以通过vmstat命令来动态获取它的变化值,比如每隔一秒采集一次,总共采集十次memory的变化;
[work@# ~]$ vmstat 1 10
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 463664 98752 36521052 0 0 157 272 1 1 4 6 90 0 0
2 0 0 462780 98752 36521060 0 0 0 12 21790 17344 3 6 90 0 0
2 0 0 458672 98752 36521076 0 0 0 20 22598 18355 3 6 90 0 0
2 0 0 439960 98752 36521076 0 0 0 84 22457 18133 4 8 88 0 0
2 0 0 447608 98752 36521084 0 0 0 28 22618 17794 4 6 90 0 0
2 0 0 443508 98752 36521100 0 0 0 24 22736 41260 6 8 87 0 0
1 0 0 444012 98752 36521064 0 0 0 52 22733 18433 3 7 90 0 0
2 0 0 450972 98752 36521064 0 0 0 0 22269 17790 3 6 91 0 0
4 0 0 452824 98752 36521076 0 0 0 52 22661 18445 3 6 91 0 0
3 0 0 453120 98752 36521092 0 0 0 8 21966 17457 4 6 90 0 0
再推荐一个命令:watch,与free命令配合,也可以获取到动态的内存变化值,case:watch -n 1 -d free
总结:从上面,我们可以看出当前系统物理被占用12G,可用与新的进程的内存高达37G;
###入门篇之top命令解析
top不仅仅用于内存分析,还用于进程分析,今天只写关于内存部分;
top - 21:41:22 up 38 days, 8:38, 1 user, load average: 0.14, 0.20, 0.25
Tasks: 320 total, 1 running, 314 sleeping, 5 stopped, 0 zombie
Cpu(s): 3.2%us, 6.6%sy, 0.6%ni, 89.1%id, 0.0%wa, 0.0%hi, 0.4%si, 0.0%st
Mem: 49406140k total, 48392788k used, 1013352k free, 96584k buffers
Swap: 0k total, 0k used, 0k free, 35985072k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20811 work 20 0 18.0g 1.3g 6992 S 13.9 2.8 2318:28 scribed
其中mem部分的total,used,free,buffers,cached与free命令显示的含义是一致的,他们都反应了整个系统的内存消耗情况;
下面我们需要从top命令开始针对单进程的内存占用情况进行分析,如pid=20811
我们知道,在linux操作系统,每个进程都独立占用一个完整的“虚拟逻辑内存”大小,对于32位系统,那么每个进程可以使用的虚拟内存大小为2^32=4G内存;注意,这是虚拟内存,而不是物理内存;
在linux c编程时,通过malloc分配一块内存,并返回一个逻辑地址,这里分配的内存即为虚拟内存,它的大小可以超过物理内存大小;只有我们对分配的内存进行写(memset等)才会真正触发物理内存的分配;
上面的VIRT=18g,即为虚拟内存占用情况,很大吧!其实不用担心,RES=1.3g才是物理分配的内存大小,scribed进程我们没有合理设置相应的参数,导致VIRT和RES相差较大;%MEM=2.8就需要关心点了,它表示当前进程占用的物理内存比例,即(1.3G/46G=2.8%);
SHR=6992共享内存区大小;在linux系统中进程之间是可以做内存共享,比如一些系统级别的so,只需要加载一次,就可以被所有的进程共享,在不涉及大的进程内存共享的应用程序,基本不需要关注这部分内存的大小;
free,vmstat,top等命令可以获取当前服务器当前swap的大小;swap空间是一部分磁盘空间,相比内存来说,性能要差很多,但是在机器内存不够的情况下,swap是保证系统正常运行的最后一段关卡了;在系统里,是通过一个swappiness的值来控制对swap使用。如果swappiness=0的时候表示最大限度使用物理内存,才物理内存为0的时间才开始使用swap空间,swappiness=100的时候表示积极的使用swap分区,并且把内存上的数据及时搬运到swap空间中。linux的基本默认设置为60(/proc/sys/vm/swappiness=60);也就是说,你的内存在使用到100-60=40%的时候,就开始出现有swap的使用。大家知道,内存的速度会比磁盘快很多,这样子会加大系统io,同时造的成大量页的换进换出,严重影响系统的性能,所以我们在操作系统层面,要尽可能使用内存,对该参数进行调整。
目前在使用的服务器上基本都是关闭swap,通过人工运维来合理分配每个机器上最大可使用内存,避免swap对服务的性能造成影响;
如果swap没有关闭,通过vmstat看到大量的swap in/out就代表系统已经发生swap,服务的性能肯定已经受到影响
###中间篇之系统历史内存使用情况解析sar命令
free,vmstat,top等命令都只能获取到系统当前的内存使用情况,但是在一些线上问题跟踪过程中,需要对历史的系统压力等case进行跟进,这个时候就需要去获取机器历史内存/IO/CPU等使用情况,这里我们就针对sar命令对历史内存使用进行分析;
sar -r 参数就可以获取到以10分钟为间隔报告自午夜起当天的内存使用情况
[work@# ~]$ sar -r
Linux 2.6.32_1-16-0-0 (nj01-pcsdata-b05.nj01.baidu.com) 11/28/2015 _x86_64_ (12 CPU)
12:00:01 AM kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit
12:10:01 AM 915200 48490940 98.15 96800 36076476 16877612 34.16
12:20:01 AM 895300 48510840 98.19 96816 36085924 16876592 34.16
12:30:01 AM 943304 48462836 98.09 97092 36048648 16877212 34.16
12:40:01 AM 932804 48473336 98.11 97100 36056224 16876204 34.16
12:50:01 AM 928852 48477288 98.12 97116 36059544 16876184 34.16
01:00:01 AM 918804 48487336 98.14 97116 36066656 16875120 34.16
01:10:01 AM 893916 48512224 98.19 97132 36073808 16876188 34.16
01:20:01 AM 865428 48540712 98.25 97148 36089332 16876404 34.16
kbmemfree kbmemused %memused kbbuffers kbcached这几个参数都比较容易理解,和上面free等命令获取的含义一直,但是这里有一个新的名词,kbcommit和%commit分别表示应用程序当前使用的内存大小和使用百分比。其实和free命令里面的第三行的used含义差不多,只是计算的方式不同而已;
###中级篇之进程status内存相关分析
通过top -p 进程号可以获取到进程的当前内存使用情况,但是比较抽象,对进程内存使用情况最完美的解析就是直接去阅读进程status内存,比如,下面带中文注释的即为内存相关的数据信息;
[work@# ~]$ cat /proc/20811/status
Name: scribed
State: S (sleeping)
Tgid: 20811
Pid: 20811
PPid: 1
TracerPid: 0
Uid: 500 500 500 500
Gid: 501 501 501 501
FDSize: 8192
Groups: 501
VmPeak: 20531460 kB //进程分配的虚拟内存峰值
VmSize: 18875052 kB //进程当前分配的虚拟内存大小
VmLck: 0 kB //进程当前加锁的内存大小,参考linux mlock
VmHWM: 1943192 kB //进程使用的物理内存峰值
VmRSS: 1357056 kB //进程当前使用的物理内存大小
VmAnon: 1350016 kB //匿名page大小,有些内存比如so占用内存都是有名称,参考meminfo的功能
VmFile: 7040 kB //so等文件占用内存大小,VmAnon+VmFile=VmRSS
VmData: 18711392 kB //进行数据段占用的虚拟内存大小
VmStk: 120 kB //进程堆栈段的虚拟内存大小
VmExe: 3652 kB //进程代码段的虚拟内存大小
VmLib: 12968 kB //进程所使用LIB库的虚拟内存大小
VmPTE: 4584 kB //占用的页表的大小.
VmSwap: 0 kB //交换分区大小
Threads: 56
SigQ: 0/385964
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000003
SigCgt: 1000000181005ccc
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: ffffffffffffffff
Cpus_allowed: fff
Cpus_allowed_list: 0-11
Mems_allowed: 00000000,00000003 //
Mems_allowed_list: 0-1 //表示进程可以使用的内存段,有点高端,就不解析含义了
voluntary_ctxt_switches: 3264080472
nonvoluntary_ctxt_switches: 794416
###中级篇之系统meminfo内存相关分析
和进程status文件一样,对系统内存的了解除了通过命令来获取以外,还可以直接对系统的meminfo文件进行阅读来了解当前系统的内存使用情况,哈哈,虽然意义不是特别大。
[work@# ~]$ cat /proc/meminfo
MemTotal: 49406140 kB //直译
MemFree: 410128 kB //直译
Buffers: 98848 kB //直译
Cached: 36568808 kB //直译
SwapCached: 0 kB //直译
Active: 26052152 kB //最近经常被使用的内存,除非非常必要否则不会被移作他用.
Inactive: 21128908 kB //最近不经常被使用的内存,非常用可能被用于其他途径.
Active(anon): 10577508 kB //匿名活动page
Inactive(anon): 396316 kB //匿名非活动page
Active(file): 15474644 kB //文件活动page,Active= Active(anon) + Active(file)
Inactive(file): 20732592 kB //文件非文件page,Inactive=Inactive(anon) + Inactive(file)
Unevictable: 1892 kB
Mlocked: 0 kB
SwapTotal: 0 kB //直译
SwapFree: 0 kB //直译
Dirty: 584 kB //脏页内存大小,页更新但是没有刷到文件的内存大小
Readahead: 0 kB
Writeback: 0 kB
AnonPages: 10515528 kB
Mapped: 114204 kB //文件map映射占用内存大小
Shmem: 458524 kB //共享内存占用的内存大小
Slab: 1214064 kB
SReclaimable: 1163692 kB
SUnreclaim: 50372 kB
KernelStack: 8792 kB
PageTables: 37736 kB //页表大小
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 24703068 kB
Committed_AS: 16887880 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 380668 kB
VmallocChunk: 34332399224 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 6152 kB
DirectMap2M: 50305024 kB
关于匿名内存和文件内存的解析:
匿名内存一般是程序内存通过malloc等分配的没有名称的内存,而文件内存>> 即为so,jar等库文件占用的内存,通过pmap 进程号 可以直接看出每个进程的匿名内存和文件内存 00007f34243f9000 4K rw--- /lib64/libnss_files-2.12.so 00007f34243fa000 12K ----- [ anon ]