话说团队的兄弟有一天问我,为啥咱唯一的一个服务器,内存都用完了,我还想在上面测性能呢。我一听,第一反应:不可能!我说你胡扯呢吧,咱那可是16G的一个物理机,上面就跑了git服务器,怎么可能把内存吃完了呢。他撇了撇嘴:不信自己上去看。
既然这么说了,我便远程登上去瞅瞅吧。第一想到的就是top命令,于是,敲了top,得到了以下的截图:
就剩下3个G了(这个还是多的,当天就剩600多M free了),确实有些反常,shift+m看一下内存占用最高的进程是啥,得到以下的截图:
看到排名第一的是mysqld,占到了11.9%(%MEM)即1.8G(RES)的物理内存;排名第二的是java,只有6.5% 1.0G的物理内存(RES是指物理内存,下面详细说明);第三就只有0.1%,这把大头加一下,才用了3G内存,总共16个G呢,剩下10个G让谁吃了?
看到这儿,我无言以对了,不是我理解错了,就是top工具撒谎了。赶紧开始google相关的东西,第一个得到的就是另外一个查看内存的工具:free:
free这个工具是专门用来显示当前物理内存以及虚拟内存情况的,后面的参数-m是让工具以MB为单位来显示数据,从这里可以非常清楚的看到,Mem的free是3856MB,确实和top显示的一样,看来top工具没有撒谎,一定是我们理解错什么了。
继续google,逐渐理解到了这样一件事儿。我们从free工具显示的结果中可以看到这么一行:-/+ buffers/cache,这里显示free为12367MB,这是什么意思呢。
这里就必须说一下linux设计内存管理时的一个想法了。linux认为,我们的内存在系统启动之后,大部分情况下都会是空闲的。既然是空闲的,那linux就琢磨,不用白不用啊,我先用它干点儿啥,等你正儿八经要用的时候我再还给你。于是,linux就开始把这部分的空闲内存都先拿过来用着,干吗用呢?linux琢磨,你硬盘不是慢么,咱就想办法把这部分内存用作硬盘的一个缓存,这样,你要访问什么文件的时候我先给你加载到内存中缓存着(比如一些程序执行的文件以及一些共享库等,这在linux内称为显式的Pages;对应的隐式的Pages指的是为运行程序保留的数据、栈空间等),以后每次你访问的时候我都从这部分内存中给你拿,不去和硬盘打交道,这速度不就大大的快了;当一个进程真正想用内存的时候,linux就会从它占用的这些缓存内存中空闲的部分分配需要的大小给你,不用担心linux一直占着不给。
知道了上面这个,就好解释free工具中那个-/+ buffers/cache行的意思了。其实这一行所表示的free才是真正的物理内存当前的空闲值,第一行Mem中的free只是我们实际的内存-内核占用-实际使用-linux拿跑做缓存后所剩下的值。另外要想判断是不是真的内存用光了,通过判断Swap也可以知道当前物理内存是否用完了。
另外,这里还有一个小细节需要提一下,free工具中Mem的total值是15900MB(和top一样),远不是我说的16GB,这是因为,其中一部分被linux内核占用了,linux内核是无法swap out的,所以这部分内存是永远不会free。
说回刚才的这个被linux占用的cache来。从我google的结果来看,其实linux占用的这个cache也不是一下全部用掉了,它也有已经被使用和free的部分,被使用的部分主要是上面提到的当前进程相关联的一些程序文件或者共享库等,还有为进程保留的栈空间和运行时数据的空间,这两个分别被称为显式的Pages(也称为文件缓存)和隐式的Pages。那么现在问题来了,如果cache中全部被这些数据占有了之后,我们再想分配内存的时候该怎么办呢?此时就得分两步来聊了:
对于显式Pages来说,既然它是文件缓存,顾名思义,它是有硬盘的文件做备份的,即使linux系统在内存不够的情况下把它从内存中清理出去了(注意不是swap out),那么我们在需要的时候还可以访问硬盘把这些数据读回来使用(可能就是会慢一些),当然如果有swap空间的情况下还是会先swap out;
对于隐式Pages来说就不好办了,因为这个如果从内存中清理出去了,就回不来了,所以不能清理,只能把它临时的swap out出去,需要的时候在swap回来;那么,当出现内存不够用且swap空间也不够用的情况时,linux就无法将隐式的Pages swap out出去,此时OOM-killer(out of memory killer)就开始工作,将一些占内存的进程干掉来释放内存给其它进程;
这个时候又有问题来了,当内存不够用的时候,到底是清理显式的Pages还是swap out隐式的Pages呢?从linux kernel 2.6.28之后开始,linux对两种Pages做了权重值来做处理这件事情:默认情况下,权重值的总值为200,系统有一个配置:vm.swappiness,用来指定隐式Pages的权重,这个值默认是60,那么对于显式的Pages来说,它的权重就是200-vm.swappiness,默认情况下就是140;当linux需要swap out内存的时候,首先处理的就是权重高的,即显式的Pages;
当我们把vm.swappiness设置成0时,linux就会尽可能的保证隐式Pages不会被swap out出去,这种设置对于需要自己做缓存的系统有非常大的性能提升;
将vm.swappiness设置成100,可以提高I/O密集型系统的性能,防止显式Pages的文件缓存被清理出去;
vm.swappiness可以通过cat /proc/sys/vm/swappiness来查看;通过sysctl -w vm.swappiness=N 或者 echo 30 >/proc/sys/vm/swappiness来修改;也可以通过修改/etc/sysctl.conf里的vm.swappiness来在linux启动时就修改。
了解了上面这些,使我突然明白了一个东西,就是在linux中(windows不知道),不设置swap空间其实不能对系统的性能有提升,反而可能下降;因为linux系统不会因为没有swap空间就不清理内存的Pages,只是当没有swap空间的时候,linux系统可能要优先处理显式的Pages,因为它有硬盘文件做备份,此时性能同样会下降;
最后,补充一下top工具显示出来的内存相关项目的意思,主要有以下几个:
VIRT:这个指的是一个进程占用内存的虚拟大小,换句话说就是该进程当前实际访问的内存大小。具体来说,除了进程自己占用的物理内存外,还包括映射给它的内存(比如X Server需要的显卡内存),硬盘的文件在内存中的映射所需的大小(大部分是一些共享库),已经swap out的大小,以及和其它进程共享的内存等;
RES:这个刚才说过了,是该进程实际占用的物理内存大小,它不包含已经swap out的大小,这个值也是后面%MEM测量所需的值,当然,这个值一定会小于上面的VIRT;
SHR:这个值得是在VIRT中,实际的共享内存和共享库所占用的内存大小,这里需要注意的是,一半一个进程只会使用某个共享库的一部分代码,但是在VIRT和SHR中,整个共享库在内存中的映射都会计算进来,但是只有实际使用的会计算到RES中。
参考文章:
Overview of memory management: http://www.linuxhowtos.org/System/Linux%20Memory%20Management.htm
Why is swappiness set to 60 by default?: http://unix.stackexchange.com/questions/88693/why-is-swappiness-set-to-60-by-default
(责任编辑:IT) |