Linux中free命令各字段详解以及内存和cache相关知识整理

今天在跑一个python排序程序的时候,想通过top命令检查程序消耗内存的变化。很奇怪,检测到的内存使用比预期的要高不少。刚好借这个机会整理下Linux中内存管理的一些知识。

free命令

通过free命令可以查看当前系统的内存使用情况,参数-h可以将单位自动转换的比较友好

[root@testmachine tools]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        1.8G        163M        9.1M        1.8G        1.6G
Swap:          2.0G        925M        1.1G

先看第一行Mem部分的内容

  • total - 物理内存的总和
  • used - 已经被应用程序使用的内存大小
  • free - 完全空闲的内存大小
  • shared - 被共享的内存
  • buff/cache - 被当作缓存使用的内存大小
  • available - 可以被新的应用程序使用的内存大小

这里可能有朋友会有疑问,你电脑3.7G的内存怎么只剩下了163M,是不是系统出了问题?或者为什么只有163M内存可用,但新的程序却又可以使用1.6G的内存?这些疑问我们下面都会解答。

再来看第二行Swap,交换区

  • total - 可被用作swap的硬盘总大小
  • used - 被使用的swap大小
  • free - 可被使用的swap大小

至于什么是交换区,有什么用,后面我们也会单独说。

内存映射与页

先来说说内存使用的基本逻辑。

每个进程都有自己的独立寻址空间,也就是说某进程创建的变量不会被另一个进程访问到。进程能寻址的地址叫做虚拟内存,针对32位寻址的机器,每个进程有2^32个寻址地址,也就是4GB,而64位寻址的机器,理论上有2^64个寻址地址。但是受限于主板和地址线个数,目前内存条基本都是16GB的(2020年7月),物理内存条对应的是物理内存

那么问题就来了,虚拟内存和物理内存之间大小明显不匹配,物理内存似乎完全不够用,这是怎么回事?

解答这个问题前,先来聊一聊页(Page)。

计算机会对虚拟内存和物理内存按照最小单位4KB进行处理,虚拟内存中的4KB称为(Page),物理内存中的4KB称为页帧(Page Frame),页和页帧之间的对应关系称为内存映射。所有的进程共享同一物理内存,每个进程也只会把目前需要的虚拟内存地址映射到物理内存中。

进程去页中查询所需要的数据,而真正的数据都在页帧中,所以还需要一个页表(Page Table)去管理这种对应关系以便进程去查找。页表的每一条记录分为两部分,第一部分记录此页是否在物理内存中,第二部分记录物理内存页帧的地址(如果存在的话)。

当进程想要访问某个虚拟地址,但是没有在页表中查到物理内存的地址,说明数据还没有读到内存中,这叫做缺页异常(Page Fault)。此时进程会将数据从硬盘读到物理内存中,并更新页表。假如从硬盘读入内存的时候,内存中的页帧都被用了,就会找一个使用较少的页帧进行覆盖。

所以就可以回答上面的问题了。虽然每个进程有自己独立的虚拟内存地址,但是只是把需要用到的部分映射到物理内存,有了新的需求再去映射新的。所以虽然所有进程都共享物理内存,也会彼此独立。

那么内存中使用完毕的页帧会被马上收回吗?答案是并不会。这也是为什么好像机器上没有运行啥程序但是还是显示free的内存很少的原因,因为大量的内存都是之前使用过,但是并没有释放的部分,这就是buffercache

buffer和cache

先来看下面这个现象。我这里有一个1.1GB大小的文件,同样是读取整个文件,第一次读取花了1.3秒,而以后再读取只需要0.2秒

[root@testmachine linkedlist]# ll -h bigfile.txt 
-rw-rw-r-- 1 fuhx fuhx 1.1G Jul 17 17:37 bigfile.txt
[root@testmachine linkedlist]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        1.7G        1.8G         17M        227M        1.7G
Swap:          2.0G        922M        1.1G
[root@testmachine linkedlist]# time cat bigfile.txt > /dev/null 

real	0m1.317s
user	0m0.006s
sys	    0m1.306s
[root@testmachine linkedlist]# time cat bigfile.txt > /dev/null 

real	0m0.236s
user	0m0.006s
sys	    0m0.229s
[root@testmachine linkedlist]# time cat bigfile.txt > /dev/null 

real	0m0.211s
user	0m0.013s
sys	    0m0.198s
[root@testmachine linkedlist]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        1.7G        729M         17M        1.3G        1.6G
Swap:          2.0G        922M        1.1G

同样值得注意的是,读取文件前后,free部分的内存下降了约1GB,而buff/cache部分的内存上升了约1GB。

这说明整个文件都被缓存在了内存中,除了第一次调用需要从硬盘读到内存,以后每次都是直接从内存读取,所以会快很多

buffer和cache一个是缓冲区一个是缓存,不过这里都可以理解成内存和硬盘之间交互的一个中间层。

page cache

针对用页来管理的内存,其缓存被称为page cache。其主要是在进程对文件进行读写操作时发挥作用。

用户读写与page cache

读文件时,用户发起read操作,进程查找页表,如果有映射到页帧,则直接返回页帧中返回的内容,如果没有匹配的页帧,则触发缺页异常,从硬盘读取数据到页帧进行缓存,read操作完成。

写文件时,用户发起write操作,进程查找页表,如果有映射到页帧,直接写到页帧中,此时的页被称为脏页(Dirty Page),如果没有匹配的页帧,则创建页帧写入数据。用户write操作完成。此后操作系统有两种方式将脏页回写到硬盘,用户手动调用fsync()或者由pdflush进程定时将脏页写回。

不管是读还是写,有对应的cache称为命中,命中率是判断某进程缓存好换的关键指标。

这也就回答了上面的疑问,电脑3.7G的内存怎么只剩下了163M,是不是系统出了问题?并不是电脑出了问题,而是Linux的内存管理机制就是这样,尽可能地对硬盘数据进行缓存,以加快读写速率。这一点跟Windows不一样。

当有新的程序要申请内存时,这些被缓存占用的内存会被释放,这就是为什么available的内存会将缓存也算进去。

当然,虽然不建议,但是如果想手动清除cache也是可以的。

手动清理cache

通过修改内核参数可以达到删除cache的目的

[root@testmachine tools]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        1.8G        163M        9.1M        1.8G        1.6G
Swap:          2.0G        925M        1.1G
[root@testmachine tools]# echo 3 > /proc/sys/vm/drop_caches 
[root@testmachine tools]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        1.7G        1.7G        9.1M        221M        1.7G
Swap:          2.0G        925M        1.1G

Linux中一切皆文件,内核的各种设置也以文件形式提供操作API,这就是/proc/sys/目录。临时的修改只需要将值echo到文件即可,因为是直接作用于内存所以马上生效,但是机器重启就会消失。永久生效需要在/etc/sysctl.conf或者是/etc/sysctl.d/xxx.conf中将目录换成点号进行赋值。例如echo 1 > proc/sys/net/ipv4/ip_forward就写成net.ipv4.ip_forward=1然后跑命令sysctl -p从文件生效

关于内核参数调优,可以参考这一篇博客,和内存相关的都在/proc/sys/vm/目录下

首先,这种清理也并不是没有成本的。前面说过用户执行写操作会产生脏页,如果存在脏页,系统要先写回到硬盘才能回收内存。所以清除缓存通常伴随着高I/O

同时可以发现cache并不会完全清除,这是因为有些cache不能被系统回收,例如tmpfs,进程间的通讯用的共享内存等等,更多细节可以参考Linux 内存中的Cache,真的能被回收么?

再次强调,Linux的缓存是不需要手动去清理的

内存使用增多排查步骤

如果发现内存在一点点被蚕食,可以按照这篇文章推荐的步骤来排查

  1. 通过 free,发现大部分内存都被缓存占用后
  2. 可以使用 vmstat 或者 sar观察一下缓存的变化趋势,确认缓存的使用是否还在继续增大。
  3. 如果继续增大,则说明导致缓存升高的进程还在运行,那你就能用缓存 / 缓冲区分析工具(比如 cachetop、slabtop 等),分析这些缓存到底被哪里占用。
  4. 排除缓存 / 缓冲区后,你可以继续用 pidstat 或者 top,定位占用内存最多的进程。
  5. 找出进程后,再通过进程内存空间工具(比如 pmap),分析进程地址空间中内存的使用情况就可以了。
  6. 通过 vmstat 或者 sar 发现内存在不断增长后,可以分析中是否存在内存泄漏的问题。比如你可以使用内存分配分析工具 memleak ,检查是否存在内存泄漏。如果存在内存泄漏问题,memleak 会为你输出内存泄漏的进程以及调用堆栈

内存泄漏:memory leak,内存使用完毕没有回收,导致该区域永远无法被后面的进程使用的现象

swap

下面来说说swap,也就是交换区。

所谓的swap,就是将一部分硬盘当作内存来使用。内存中不常用的一部分会被交换到swap,下次再使用的时候再交换回内存,分别叫做换出和换入。

既可以在分区的时候就设定一块盘做为swap,例如我电脑这样

[root@testmachine shm]# fdisk -l

Disk /dev/sda: 53.7 GB, 53687091200 bytes, 104857600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x000c328c

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048     2099199     1048576   83  Linux
/dev/sda2         2099200    41943039    19921920   8e  Linux LVM
/dev/sda3        41943040   104857599    31457280   83  Linux

Disk /dev/mapper/centos-root: 49.4 GB, 49387929600 bytes, 96460800 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mapper/centos-swap: 2147 MB, 2147483648 bytes, 4194304 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

或者像下面这样创建一个文件做为swap。

文件作为swap分区

1.创建要作为swap分区的文件:增加1GB大小的交换分区,则命令写法如下,其中的count等于想要的块的数量(bs*count=文件大小)。
dd if=/dev/zero of=/root/swapfile bs=1M count=1024

2.格式化为交换分区文件:
mkswap /root/swapfile #建立swap的文件系统

3.启用交换分区文件:
swapon /root/swapfile #启用swap文件

4.使系统开机时自启用,在文件/etc/fstab中添加一行:
/root/swapfile swap swap defaults 0 0

swappiness

有一个内核参数如下

[root@testmachine shm]# cat /proc/sys/vm/swappiness 
30

这是一个介于0-100的配置项,用来表示系统进行内存回收时候的偏好。数值越大,在内存回收时越偏好使用swap,也就是将一些不常用的页换出;数值约小,在内存回收时越拒绝使用swap,也就是更倾向将缓存直接释放或者脏页写回硬盘再释放。

通常为了提升性能,建议将这个值设置为0。

内存管理优化

这篇文章中还推荐了很多内存优化的工具,像cachetop,vmstat等,以及一些优化思路。做为补充内容放在这里了。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值