在 Linux 的内存分配机制中,优先使用物理内存,当物理内存还有空闲时,不会释放其占用内存,就算占用内存的程序已经被关闭了,该程序所占用的内存用来做缓存使用,这样对于开启过的程序、或是读取刚存取过得数据会比较快,可以提高整体 IO 效率。
free 命令
内存使用情况可以通过 free 命令查看,在 CentOS 中,是 procps-ng 包中的一个程序,该命令实际是读取 /proc/meminfo 文件,对应内核源码中的 fs/proc/meminfo.c。
$ free
total used free shared buffers cached
Mem: 386024 377116 8908 0 21280 155468
-/+ buffers/cache: 200368 185656
Swap: 393552 0 393552
最新版本的 free 会将 buffer/cache 展示在一起,可以通过 free -w 分开查看 buffers 以及 cache;默认的单位是 KB 。
Mem 表示物理内存统计
total : 物理内存总大小;
used : 已经分配的内存总计(含buffers 与cache ),但其中部分可能尚未使用;
free : 未被分配的内存;
shared : 多个进程共享的内存总额;
buffers: 系统分配但未被使用的 buffers 数量,磁盘buffers数量;
cached: 系统分配但未被使用的 cache 数量,磁盘cache数量;
-/+ buffers/cached 表示物理内存的缓存统计
used: 实际使用的 buffers 与 cache 总量(used-buffers-cached),也是实际使用的内存总量;
free: 未被使用的 buffers 与 cache 和未被分配的内存之和(buffers+cached+free),这就是系统当前实际可用内存;
Swap 表示硬盘上交换分区的使用情况
只有 mem 被当前进程实际占用完,即没有了 buffers 和 cache 时,才会使用到 swap 。
其实第一行可以从操作系统的角度去看,即 OS 认为自己总共有 total 的内存,分配了 used 的内存,还有 free 的内存;其中分配的内存中分别有 buffers 和 cached 的内存还没有使用,也就是说 OS 认为还有空闲的内存,又懒得收回之前分配,但已经分配的不能再使用了。
第二行可以认为从进程的角度去看,可用的内存包括了空闲的+buffers+cached,total-空闲的就是时用的内存数。
可以通过如下方式计算:
----- 实际可用内存大小
Free(-/+ buffers/cache) = Free(Mem) + buffers(Mem) + Cached(Mem)
185656 = 8908 + 21280 + 155468
----- 物理内存总大小
total(Mem) = used(-/+ buffers/cache) + free(-/+ buffers/cache)
386024 = 200368 + 185656
= used(Mem) + free(Mem)
= 377116 + 8908
----- 已经分配的内存大小
Used(Mem) = Used(-/+ buffers/cache) + buffers(Mem) + Cached(Mem)
377116 = 200368 + 21280 + 155468
关于内存的其它信息可以参考如下文件。
/proc/meminfo // 机器的内存使用信息
/proc/pid/maps // 显示进程 PID 所占用的虚拟地址
/proc/pid/statm // 进程所占用的内存
/proc/self/statm // 当前进程的信息
新版
如上所述,新版的命令行输入如下。
$ free -w
total used free shared buffers cache available
Mem: 8070604 2928928 495012 1162676 104156 4542508 3624840
Swap: 8127484 232388 7895096
total (MemTotal @ /proc/meminfo)
free (MemFree @ /proc/meminfo)
shared (Shmem @ /proc/meminfo) 主要是tmpfs使用
buffers (Buffers @ /proc/meminfo)
cache (Cached+Slab @ /proc/meminfo)
available (MemAvailable @ /proc/meminfo)
used = total - free - buffers - cache (注意,没有去除shared)
可以通过 free -k -w && cat /proc/meminfo
命令进行测试。
Swap对性能的影响
当内存不足的时候把磁盘的部分空间当作虚拟内存来使用,而实际上并非是在内存不足的时候才使用,有个参数叫做 swappiness,是用来控制 swap 分区使用的,可以直接查看 /proc/sys/vm/swappiness
文件。
默认值是 60,范围是 [0, 100];为 0 表示会最大限度使用物理内存,然后才是 swap;为 100 时,表示尽量使用 swap 分区,把内存上的数据及时的搬运到 swap 空间里面。
分配太多的 Swap 空间会浪费磁盘空间,而 Swap 空间太少,则系统有可能会发生错误。当系统的物理内存用光了,系统就会跑得很慢,但仍能运行;但是如果 Swap 空间也用光了,那么系统就会发生错误。
通常情况下,Swap 空间是物理内存的 2~2.5 倍,最小为 64M;这也根据不同的应用,有不同的配置:如果是桌面系统,则只需要较小的 Swap 空间;而大的服务器则视情况不同需要不同大小的 Swap 空间,一般来说对于 4G 以下的物理内存,配置 2 倍的 swap ,4G 以上配置 1 倍。
另外, Swap 分区的数量对性能也有很大的影响。因为 Swap 交换的操作是磁盘 IO 的操作,如果有多个 Swap 交换区, Swap 空间的分配会以轮流的方式操作于所有的 Swap ,这样会大大均衡 IO 的负载,加快 Swap 交换的速度。
实际上,当空闲物理内存不多时,不一定表示系统运行状态很差,因为内存的 cache 及 buffer 部分可以随时被重用。只有 swap 被频繁调用,才是内存资源是否紧张的依据,可以通过 vmstat 查看。
状态查看
常用命令
介绍下与内存相关的命令。
pmap
可以根据进程查看进程相关信息占用的内存情况,可以通过 -x
、-X
、-XX
查看详细信息。
$ pmap -x $(pidof mysqld)
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 37116 9120 0 r-x-- mysqld
0000000002a3e000 872 192 12 r---- mysqld
0000000002b18000 700 300 196 rw--- mysqld
0000000002bc7000 772 628 628 rw--- [ anon ]
00007f0df23ff000 4 0 0 ----- [ anon ]
00007f0df2400000 53248 8472 8472 rw--- [ anon ]
00007f0df5bfc000 4 0 0 ----- [ anon ]
... ...
00007fff995ca000 132 76 76 rw--- [ stack ]
00007fff995f2000 8 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------- ------- -------
total kB 705216 141876 129408
输出值: 显示扩展格式
Address : 虚拟内存开始地址;
Kbytes : 占用内存的字节数,单位是KB;
RSS : (Resident Set Size)驻留内存的字节数(KB);
Dirty : 脏页的字节数(包括共享和私有的)(KB);
Mode : 内存页的权限(r=read, w=write, x=execute, s=shared, p=private);
Mapping : 占用内存的文件、[anon] (分配的内存)、[stack] (堆栈);
$ pmap -d $(pidof mysqld)
Address Kbytes Mode Offset Device Mapping
0000000000400000 23288 r-x-- 0000000000000000 0ca:00001 mysqld
0000000001cbe000 940 r---- 00000000016be000 0ca:00001 mysqld
0000000001da9000 680 rw--- 00000000017a9000 0ca:00001 mysqld
0000000001e53000 888 rw--- 0000000000000000 000:00000 [ anon ]
0000000001f31000 11680 rw--- 0000000000000000 000:00000 [ anon ]
00007fd784000000 4248 rw--- 0000000000000000 000:00000 [ anon ]
00007fd784426000 61288 ----- 0000000000000000 000:00000 [ anon ]
00007fd78c000000 132 rw--- 0000000000000000 000:00000 [ anon ]
00007fd78c021000 65404 ----- 0000000000000000 000:00000 [ anon ]
00007fd7d2c67000 12 rw-s- 0000000000000000 000:0000a [aio] (deleted)
00007fd7d2c6a000 12 rw-s- 0000000000000000 000:0000a [aio] (deleted)
00007fd7d2c6d000 4 rw-s- 0000000000000000 000:0000a [aio] (deleted)
00007fd7d2c6e000 4 rw--- 0000000000000000 000:00000 [ anon ]
00007fd7d2c6f000 4 r---- 000000000001f000 0ca:00001 ld-2.17.so
00007fd7d2c70000 4 rw--- 0000000000020000 0ca:00001 ld-2.17.so
00007fd7d2c71000 4 rw--- 0000000000000000 000:00000 [ anon ]
00007ffc8fef3000 132 rw--- 0000000000000000 000:00000 [ stack ]
mapped: 1248492K writeable/private: 545308K shared: 128K
输出值: 显示设备格式
Offset: 文件偏移
Device: 设备名 (major:minor)
最后一行输出:
mapped : 该进程映射的虚拟地址空间大小,也就是该进程预先分配的虚拟内存大小,即ps出的vsz;
writeable/private : 进程所占用的私有地址空间大小,也就是该进程实际使用的内存大小,不含shared libraries;
shared : 进程和其他进程共享的内存大小;
假设上述结构导出到了/tmp/1文件中,可以通过如下命令计算:
mapped: 所有行Kbytes字段的累加
awk 'BEGIN{sum=0};{sum+=$2};END{print sum}' /tmp/1
writeable/private: 模式为w+p,且不是s的内存
awk 'BEGIN{sum=0};{if($3~/w/ && $3!~/s/) {sum+=$2}};END{print sum}' /tmp/1
shared: 共享内存数
awk 'BEGIN{sum=0};{if($3~/s/) {sum+=$2}};END{print sum}' /tmp/1
vmstat
vmstat 不会将自己作为一个有效的进程。
$ vmstat [options] [delay [count]]
常用选项:
delay: 每多少秒显示一次。
count: 显示多少次,默认为一次。
-a: 显示活跃和非活跃内存。
-f: 显示从系统启动之后 forks 的数量;包括了 fork, vfork, clone 的系统调用,等价于创建的全部任务数。
-m,--slabs: 显示 slabinfo 。
-n,--one-header: 只显示一次,而不是周期性的显示。
-s,--stats: 显示内存统计以及各种活动信息。
-d,--disk: 显示磁盘的相关统计。
-D,--disk-sum: 显示硬盘统计信息的摘要。
-p,--partition device: 显示某个磁盘的统计信息。
-S,--unit char: 显示输出时的单位,1000(k),1024(K),1000*1000(m),1024*1024(M),默认是 K,不会改变 swap 和 block 。
----- 每秒刷新一次
$ vmstat -S M 1
procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
3 0 0 1963 607 2359 0 0 0 0 0 1 32 0 68 0
各个段的含义为:
procs 进程
r: 等待运行的进程数,如果运行队列过大,表示CPU很忙,一般会造成CPU使用率很高。
b: 不可中断睡眠(uninterruptible sleep)的进程数,也就是被阻塞的进程,通常为IO。
memory 内存
swpd: 虚拟内存(Virtual Memory)的使用量,应该是交换区。</li><li>
free: 空闲内存数,同free命令中的free(Mem)。</li><li>
buff: 同free命令,已分配未使用的内存大小。</li><li>
cache: 同free命令,已分配未使用的内存大小。</li><li>
inact: 非活跃(inactive)内存的大小,使用 -a 选项????。</li><li>
active: 活跃(active)内存的大小,使用 -a 选项????。
swap 交换区
si: 每秒从交换区(磁盘)写到内存的大小。
so: 每秒写入交换区(磁盘)的大小。
如果这两个值长期大于0,系统性能会受到影响,即我们平常所说的Thrashing(颠簸)。
IO
bi: 每秒读取的块数(blocks/s)。
bo: 每秒写入的块数(blocks/s)。
system 系统
in: 每秒的中断数,包括时钟中断。
cs: 每秒上线文的交换数。
CPU
us: 用户进程执行时间。
sy: 系统进程执行时间。
id: 空闲进程执行时间,2.5.41 之后含有 IO-wait 的时间。
wa: 等待 IO 的时间,2.5.41 之后含有空闲任务。
vmtouch
通过 vmtouch offical site 可以查看文件在内存中的占比,可以下载 本地源码 。
使用参数可以直接通过 vmtouch -h
查看,如下是简单的示例:
----- 查看/etc目录下有多少在缓存中
$ vmtouch /etc/
Files: 2844
Directories: 790
Resident Pages: 274/9971 1M/38M 2.75%
Elapsed: 0.15243 seconds
----- 查看一个大文件在缓存中的比例
$ vmtouch -v big-dataset.txt
big-dataset.txt
[ ] 0/169321
Files: 1
Directories: 0
Resident Pages: 0/169321 0/661M 0%
Elapsed: 0.011822 seconds
----- 缓存部分内容到内存中,分别对应了文本和二进制文件
$ tail -n 10000 big-dataset.txt > /dev/null
$ hexdump -n 102400 big-dataset.txt > /dev/null
----- 再次查看缓存的文件,也就是62页在内存中
$ vmtouch -v big-dataset.txt
big-dataset.txt
[o ] 62/169321
Files: 1
Directories: 0
Resident Pages: 62/169321 248K/661M 0.0366%
Elapsed: 0.003463 seconds
----- 将文件加载到内存中
$ vmtouch -vt big-dataset.txt
big-dataset.txt
[OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO] 169321/169321
Files: 1
Directories: 0
Touched Pages: 169321 (661M)
Elapsed: 1.7372 seconds
----- 手动从内存中删除
$ vmtouch -ve big-dataset.txt
Evicting big-dataset.txt
Files: 1
Directories: 0
Evicted Pages: 42116 (164M)
Elapsed: 0.076824 seconds
----- 将目录下的所有文件锁定到内存中
$ vmtouch -dl /var/www/htdocs/critical/
在 Linux 中,主要是通过 posix_fadvise() 来清除缓存;通过 mincore() 判断页是否存在于缓存中。
smem
Linux 使用的是虚拟内存 (virtual memory),因此要准确的计算一个进程实际使用的物理内存就比较麻烦,如下是在计算内存使用率时比较重要的指标:
- RSS (Resident Set Size) 表示进程占用的物理内存大小,可以使用 top 命令查询;不过将各进程的 RSS 值相加,通常会超出整个系统的内存消耗,这是因为 RSS 中包含了各进程间共享的内存。
- PSS (Proportional Set Size) 它将共享内存的大小进行平均后,再分摊到各进程上去,相比来说更准确。
- USS (Unique Set Size) 也就是 PSS 中只属于本进程的部分,只计算了进程独自占用的内存大小,不包含任何共享的部分。
可以通过 smem 查看,这是一个 Python 写的程序,该程序也可以通过 matplotlib 输出图片;另外,同时提供了一个 smemcap.c 程序,可以打包嵌入式的相关数据,然后通过 -S/--source
指定打包文件。
在 CentOS 中,可以通过如下命令安装。
# yum --enablerepo=epel install smem python-matplotlib
进程内存使用量
简单介绍下如何查看各个进程的内存使用量。
top
可以直接使用 top 命令后,查看 %MEM
的内容,表示该进程内容使用比例,可以通过 -u 指定用户,也就是只监控特定的用户。
常用的命令有。
P: 按%CPU使用率排行
T: 按TIME+排行
M: 按%MEM排行
ps
如下例所示:
$ ps -e -o 'pid,comm,args,pcpu,rsz,vsz,stime,user,uid' | sort -nrk5
其中 rsz 为实际内存,上例实现按内存排序,由大到小。
小结
可以通过 free 查看整个系统内存的使用情况,free(Mem) 过小不会对系统的性能产生影响,实际剩余的内存数为 free(-/+ buffers/cache)。
通过 vmstat 1 3 输出的 swap(si/so) 可以查看交换区的切入/出情况,如果长期不为 0,则可能是因为内存不足,此时应该查看那个进程占用内存较多。
参考
- 关于内存的介绍可以参考 What every programmer should know about memory,包括了从硬件到软件的详细介绍,内容非常详细。
- 关于 Linux 内存的介绍可以参考 Linux ate my ram! 。
- 关于 Buffer Cache 和 Page Cache 的区别,比较经典的是内核开发者 Robert Love 的相关介绍 What is the major difference between the buffer cache and the page cache? Why were they separate entities in older kernels? Why were they merged later on?。