1. 内存的使用分配:
a). 进程使用的物理内存: find /proc/ -maxdepth 1 -iname "[0-9]*" | xargs -I{} cat {}/smaps | grep Pss: | awk '{s =$2}END{print s}' b). slab分配占用的内存,采用slab机制主要是解决申请时候浪费page的问题,这一部分的内存并不是application 所占用的,所以要单独列出来, 可以在meminfo 中查看到其占用空间以及可回收空间大小. c). pagetable在虚拟地址到物理地址的转换中发挥着关键的作用,所以也不属于application占用的内存,属于系统所用,所以也单独列出来. 其大小随着内存的变大而变大,可以在meminfo 中找到占用的大小. d). free的内存,这一部分内存是从system的角度看,依然是free的,也就是说这一部分内存还没有被system 进行接管. e). cache/buffer内存的大小,这一部分可以在meminfo 中找到,这里主要是 application 的所使用的cache/buffer. f). 其他原因导致的内存gap, 在下面的示例中,上述所述的6种内存的总和大于实际的总内存,这是因为 shmem 是被application使用的,所以在计算进程使用的物理内存的时候,已经包含了shmem,而cache又计算了一次,因此最后的结果应该是减去SHMEM, 这样 和总内存相比,还有5497KB的gap .那么这个gap 到底应该是available的,还是算作used的,不得而知,那么因为这个gap 不大,所以对于内存的使用状况统计,我们可以暂且忽略该gap, 所以我们可以有如下的公式作为一个参考: total = free cache buffer process_used_via_pss slab pagetables - shmem
代码语言:javascript复制[root@localhost proc]# find /proc/ -maxdepth 1 -iname "[0-9]*" | xargs -I{} cat {}/smaps | grep Pss: | awk '{s =$2}END{print s}';grep -Ei "slab|srecl|su|pagetables|free|cache|buff|total|available|shmem" /proc/meminfo
cat: /proc/23234/smaps: No such file or directory
745019
MemTotal: 1863040 kB
MemFree: 177220 kB
MemAvailable: 821624 kB
Buffers: 124 kB
Cached: 760024 kB
SwapCached: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Shmem: 41308 kB
Slab: 181208 kB
SReclaimable: 112964 kB
SUnreclaim: 68244 kB
PageTables: 35256 kB
VmallocTotal: 34359738367 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Surp: 0
[root@localhost proc]# python
Python 2.7.5 (default, Apr 2 2020, 13:16:51)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 760024 124 745019 177220 181208 35256-1863040-41308
-5497
2. 计算系统可用内存:
根据上面对内存分配的分析,要计算可用内存,基本可以用如下的公式: available = free buffer cache - shmem (slab - SUnreclaim) 但是上述计算的结果会大于系统meminfo 所report得available很多, 这个是因为除了 SHMEM属性的cache 不可以被reclaim, 还有一部分 cache 依然不可以被reclaim. 那么这一部分的内存大小是多少呢? 当我们用 echo 3 >/proc/sys/vm/drop_caches 进行cache释放以后,我们可以看到依然有部分的cache无法释放,也就是说这一部分cache是无法被回收利用的,因此,真正的available 还需要减去 这一部分。 所以变更的公式为: available = free buffer page_cache - shmem SReclaimable - cached_cannot_release_by_drop
3. 进程使用内存的分析:
对每个进程的内存使用,用 cat /proc/PID/smaps 可以看到一个进程详细的内存分配信息,其中至少有如下的几个参数需要熟悉:
代码语言:javascript复制7f5c4fdc6000-7f5c4fdcf000 r-xp 00000000 fd:00 160815 /usr/lib64/libwrap.so.0.7.6
Size: 36 kB #该段内存地址空间的大小,也就是可以以内存地址方式访问的范围
Rss: 16 kB #表示在物理内存中的地址空间的大小
Pss: 3 kB #因为有shared的内存,所以这里是把shared 的物理内存按照 均分原则进行调整后占用的所有的物理内存。 Pss= Private RSS Shared RSS / Qty_of_proces_using_shared , 当进程停止后,如果shared 占用的内存依然被其他进程使用,那么会重新计算其他进程所用的shared 的rss. 并不会release shared的内存,这一点在内存分析时候要注意。
如果要计算进程所用的私有RSS, 需要自己利用 cat /proc/PID/smaps的结果,通过属性来判断,然后计算其私有的内存占用值,当然也可以通过第三方工具获得该值.
4.swappiness值的理解:
在linux 系统中,有一个参数swappiness,这个值默认为60, 可以调整为0到100之间的任意值。这个并不是表示当剩余内存达到多少得时候会发生交换。要理解这个值得真正意义,需要理解内存回收的一点知识: 当系统认为内存不足的时候,就会发生内存回收,而这种回收有两种方式,其一是: 把当前buffer中的脏数据写回到磁盘,然后清空buffer, 还有就是把cache中的数据直接清空,然后就有可用内存了,简言之就是通过buff/cache进行回收;其二是:通过一定算法,把进程暂时不访问的内存交换到swap分区去,从而腾出内存,保证可用内存的充足. 而如何以最优的方式使用这两种回收机制是内存管理必须考虑的问题,这时候swappiness就诞生了,通过这个swappiness 的值可以调节两种回收机制的优先级. 如果该值为100, 那么表示 以同等优先级来进行回收,这样因为swap 的操作一定会产生大量的IO操作,所以必然导致对IO的影响. 系统的默认值60就表示 swap交换方式的优先级略低于 buff/cache的方式(假设也为100,实际上并没有这个值)进行内存回收. 那么该值减小到0,是不是就代表不会发生swap 方式的回收了呢?不是的. 当系统在buffer/cache方式回收之后依然无法满足可用内存的时候,就必然导致swap方式的回收。所以该值仅仅代表了系统更倾向于使用哪种方式来进行内存回收.
5. kswapd0 进程和zone, watermark:
内存大小从早期的KB增长到如今的GB, 发生了巨大变化,这其中伴随着内存地址长度的不断变化,在目前的内存地址结构中(64bit系统),基于地址范围划分了三个zone, 分别为 DMA zone, DMA32 zone, Normal zone. 其中DMA 大小是低端的16M区域, 采用slab分配器进行分配,用于kernel。 DMA32是32bit 长度地址可以访问的区域,也就是16M到4GB之间的区域。剩下从4GB到最大值属于normal zone. 在内存的管理中,可以认为是基于zone来进行的,如果系统的内存小于4GB,那么就没有normal zone. 要查看内存的zone信息,用cat /proc/zoneinfo 就可以了。 每个zone的内存都需要即使保证足够的可用内存,毕竟每个zone的作用不一样啊. 查看zone的信息知道,可以看到如下的部分结果,其中的free 表示该zone当前的free pages, 而min, low, high 表示该zone配置的值,并不是当前的值,这些配置的值有一个比较专业的名字叫做watermark, 所以其表示的分别是每个zone的watermark_min, watermark_low, watermark_high ,这三个值的意义就和kswapd0 有关系. 因为每个zone都有这三个值,所以 把所有zone的相同的值加起来就是系统内存的watermark_min, watermark_low, watermark_high, 其单位是page, 也就是4096B. 而对应 于系统的watermark_min, 有一个kernel参数:/proc/sys/vm/min_free_kbytes, 可以手动验证下,计算的系统 watermark_min 和该参数的结果是一致的. 说了这么多,kswapd0 和 系统的watermark 到底有什么关系呢? a. 当系统中可用内存小于 min_free_kbytes 时候,会触发同步回收内存, 也就是等待内存回收完成才会进行内存分配,这时候会导致系统很卡,因为在回收完成之前没有内存分配,系统当然就卡了. 而进程kswapd0 就是用来进行内存回收的,所以min_free_kbytes的大小要确保kswapd0 进程是可以被成功调起的,否则kswapd0就无法进行内存回收的操作. 这大概就是 min_free_kbytes 的意义所在吧. 不建议修改 min_free_kbytes的值,否则可能导致系统崩溃. 该值一般是系统自动计算得出的. b. 系统中可用内存介于 min_free_kbytes 以及watermark_low 之间的时候,会触发异步内存回收,也就是说给应用程序分配内存不需要等待回收完成. 这时候会唤醒 kswapd0进程进行内存回收。 既然在低于 low的时候就唤醒了kswapd0,那么确保min_free_kbytes 可以提供kswapd0唤醒所需内存有何意义呢? 如果发生了瞬间的大内存请求,那么可能一下子从高于low的状态变成低于min的状态, 那么此时就变成了在低于min的状态唤醒kswapd0了. c. 可用内存介于low 和 high 的watermark 之间,如上所述,当低于low的时候就唤醒kswapd0, 当可用内存高于high的时候,那么就kswapd0就进入了sleep 状态. d. 总结: 虽然了解了kswapd0的作用,但是遇到kswapd0使用cpu比较高的情况,似乎也没有什么可以做的,只是说明此时内存低于low的值,如果kswapd0经常长时间处于running 状态,也说明系统内存不够,需要向老板报告添加内存了.
代码语言:javascript复制[root@localhost vm]# cat /proc/zoneinfo |grep -Ei ^Node -A4
Node 0, zone DMA
pages free 1928
min 96
low 120
high 144
--
Node 0, zone DMA32
pages free 29666
min 11167
low 13958
high 16750
6. cache,buffer 的理解:
在古老的时代,cache 就是低俗设备的读缓存,而buffer就是低速设备的写缓存。其实在现在的linux系统的kenel里面,cache和buffer的意义已经发生了变化:
代码语言:javascript复制 Buffers %lu
Relatively temporary storage for raw disk blocks that shouldn't get tremendously large (20MB or so).
Cached %lu
In-memory cache for files read from the disk (the page cache). Doesn't include SwapCached.
从上面的解释来看,buffer 是对raw disk 的block 上的数据的临时保存,主要是针对采用“block访问”方式的数据块的缓存。 而cache 是对于从磁盘上读取的文件的缓存,是针对的以“page” 方式访问的数据的缓存. 工作中经常遇到的一个问题是: linux 的cache 占用非常高,有如下的两种参考方案:
- 修改kernel 参数 /proc/sys/vm/drop_caches 的值,从而清空cache. 值为1 表示清理page cache, 值为2 表示清理inode, dentries cache. 值为3表示都进行清理.
- vfs_cache_pressure , 该参数表示kernel采用什么样的优先级来清理inode 以及dentries 的cache. 默认值为100, 如果值越大,那么越倾向于释放 inode 以及dentries 的cache. 越小越倾向于保留.