CPU 管理一样,内存管理也是操作系统最核心的功能之一。内存主要用来存储系统和应 用程序的指令、数据、缓存等。
Linux 内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的。 这样,进程就可以很方便地访问内存,更确切地说是访问虚拟内存。
虚拟地址空间内部
比如最常见的 32 位和 64 位,如下所示:
进程在用户态时,只能访问用户空间内存;只有进入内核态后,才可以访问内核空间内存。虽然每个进程的地址空间都包含了内核空间,但这些内核空间,其实关联的都是相同的物理内存。这样,进程切换到内核态后,就可以很方便地访问内核空间内存。
既然每个进程都有一个这么大的地址空间,那么所有进程的虚拟内存加起来,自然要比实际的物理内存大得多。
所以,并不是所有的虚拟内存都会分配物理内存,只有那些实际使用的虚拟内存才分配物理内存,并且分配后的物理内存,是通过内存映射来管理的。
内存映射,其实就是将虚拟内存地址映射到物理内存地址。为了完成内存映射,内核为每个进程都维护了一张页
表,记录虚拟地址与物理地址的映射关系,如下图所示:
以 32 位系统为例,如下图所示:
通过这张图你可以看到,用户空间内存,从低到高分别是五种不同的内存段。
在这五个内存段中,堆和文件映射段的内存是动态分配的。
Swap 是把一块磁盘空间或者一个本地文件,当成内存来使用。它包括换出和换入两个过程。
一个很典型的场景就是,即使内存不足时,有些应用程序也并不想被 OOM 杀死,而是希望能缓一段时间,等待人工介入,或者等系统自动释放其他进程的内存,再分配给它。除此之外,我们常见的笔记本电脑的休眠和快速开机的功能,也基于 Swap 。休眠时,把系统的内存存入磁盘,这样等到再次开机时,只要从磁盘中加载内存就可以这样就省去了很多应用程序的初始化过程,加快了开机速度。话说回来,既然 Swap 是为了回收内存,那么** Linux**到底在什么时候需要回收内存呢?前面一直在说内存资源紧张,又该怎么来衡量内存是不是紧张呢?
一个最容易想到的场景就是,有新的大块内存分配请求,但是剩余内存不足。这个时候系统就需要回收一部分内
存,进而尽可能地满足新内存请求。这个过程通常被称为 直接内存回收 。
除了直接内存回收,还有一个专门的内核线程用来 定期回收内存 ,也就是 kswapd0。为了衡量内存的使用情况,
kswapd0 定义了三个内存阈值(watermark,也称为水位),分别是页最小阈值(pages_min)、页低阈值
(pages_low)和页高阈值(pages_high)。剩余内存,则使用 pages_free 表示
kswapd0 定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作。
存回收,直到剩余内存大于高阈值为止。
/proc/sys/vm/min_free_kbytes 来间接设置。min_free_kbytes 设置了页最小阈值,而其他两个阈值,都是根据页最
小阈值计算生成的,计算方法如下 :
pages_low = pages_min*5/4
pages_high = pages_min*3/2
显示系统内存情况
[root@centos7-2 ~]# free
total used free shared buff/cache available
Mem: 999720 550508 283544 6844 165668 279124
Swap: 2097148 0 2097148
所有数值默认都是以字节(kb)为单位
你可以看到,free 输出的是一个表格,其中的数值都默认以字节为单位。表格总共有两行 六列,这两行分别是物理
内存 Mem 和交换分区 Swap 的使用情况,而六列中,每列数据 的含义分别为:
简写 | 完成写法 | 作用 |
---|---|---|
-b | –bytes | 所有数值单位是bytes |
-k | –kibi | 所有数值单位是kb |
-m | –mebi | 所有数值单位是mb |
-g | –gibi | 所有数值单位是gb |
-h | –human | 人性化输出 |
-W | –wide | 将buff和cache拆分为单独的两个列显示 |
-C | –count | 统计次数,默认是1次 |
-l | –lohi | 显示详细的低内存和高内存统计信息 |
-S | –seconds | 每次统计数据间隔时间 |
-t | –total | 添加一行显示数据总和 |
–help | ||
帮助文档 | ||
-V | –version | 版本号 |
举例:
每隔 2s 输出一次统计信息,总共输出 2 次,并且人性化输出所有数值
[root@centos7-2 ~]# free -h -c 2 -s 2
total used free shared buff/cache available
Mem: 976M 537M 276M 6.7M 161M 272M
Swap: 2.0G 0B 2.0G
total used free shared buff/cache available
Mem: 976M 537M 276M 6.7M 161M 272M
Swap: 2.0G 0B 2.0G
[root@centos7-2 ~]#
[root@centos7-2 ~]# top
top - 11:49:39 up 50 min, 1 user, load average: 0.12, 0.08, 0.06
Tasks: 104 total, 2 running, 102 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.2 us, 0.2 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 999720 total, 283960 free, 549672 used, 166088 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 279848 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1554 root 20 0 157836 2180 1540 R 0.3 0.2 0:00.05 top
1 root 20 0 125248 3716 2508 S 0.0 0.4 0:02.84 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:00.02 ksoftirqd/0
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
6 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kworker/u256:0
7 root rt 0 0 0 0 S 0.0 0.0 0:00.03 migration/0
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh
9 root 20 0 0 0 0 R 0.0 0.0 0:00.97 rcu_sched
10 root rt 0 0 0 0 S 0.0 0.0 0:00.07 watchdog/0
11 root rt 0 0 0 0 S 0.0 0.0 0:00.02 watchdog/1
12 root rt 0 0 0 0 S 0.0 0.0 0:00.01 migration/1
13 root 20 0 0 0 0 S 0.0 0.0 0:00.04 ksoftirqd/1
15 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/1:0H
17 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kdevtmpfs
18 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 netns
19 root 20 0 0 0 0 S 0.0 0.0 0:00.00 khungtaskd
20 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 writeback
21 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kintegrityd
22 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 bioset
23 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kblockd
24 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 md
第二行:任务进程信息
total
:系统全部进程的数量running
:运行状态的进程数量sleeping
:睡眠状态的进程数量stoped
:停止状态的进程数量zombie
:僵尸进程数量第三行:CPU信息
us
:用户空间占用CPU百分比sy
:内核空间占用CPU百分比ni
:已调整优先级的用户进程的CPU百分比id
:空闲CPU百分比,越低说明CPU使用率越高wa
:等待IO完成的CPU百分比hi
:处理硬件中断的占用CPU百分比si
:处理软中断占用CPU百分比st
:虚拟机占用CPU百分比第四行:物理内存信息
以下内存单位均为**MB **
第五行:交换区内存信息
**注:**如果used
不断在变化, 说明内核在不断进行内存和swap的数据交换,说明内存真的不够用了
在查看 top
输出时,你还要注意两点。
第一,虚拟内存通常并不会全部分配物理内存。从上面的输出,你可以发现每个进程的虚 拟内存都比常驻内存大得多
第二,共享内存SHR
并不一定是共享的,比方说,程序的代码段、非共享的动态链接库,也都算在 SHR
里。当然SHR也包括了进程间真正共享的内存。所以在计算多个进程的内存使用时,不要把所有进程的 SHR
直接相加得出结果。
缓存是Buffer
和Cache
两部分的总和Buffer
和 Cache
的设计目的,是为了提升系统的 I/O
性能。它们利用内存充当起慢速磁盘与快速 CPU
之间的桥梁,可以加速 I/O
的访问速度。Buffer
和 Cache
分别缓存的是对磁盘和文件系统的读写数据。
[root@centos7-2 ~]# free
total used free shared buff/cache available
Mem: 999720 542500 291348 6876 165872 290028
Swap: 2097148 0 2097148
字面意思,Buffer是缓存区,Cache 是缓存,两者都是数据在内存中的临时存储
Buffer:内核缓冲区用到的内存,对应的是 /proc/meminfo
中的Buffer值;
Cache: 内核页缓存和 Slab 用到的内存,对应的是 /proc/meminfo
中的 **Cached **与 Slab之和;
Buffer
Cached
/proc
是 Linux 内核提供的一种特殊文件系统,是用户跟内核交互的接口。比方说,用户可以从 /proc
中查询内核的运行状态和配置选项,查询进程的运行状态、统计数据等,也可以通过/proc
来修改内核的配置缓存命中率,是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比。
命中率越高,表示使用缓存带来的收益越高,应用程序的性能也就越好。
下面是查看系统缓存命中情况的工具:
cachestat
提供了整个操作系统缓存的读写命中情况。cachetop
提供了每个进程的缓存命中情况。$ cachestat 1 3
TOTAL MISSES HITS DIRTIES BUFFERS_MB CACHED_MB
2 0 2 1 17 279
2 0 2 1 17 279
2 0 2 1 17 279
TOTAL
:表示总的 I/O
次数;MISSES
:表示缓存未命中的次数;HITS
:表示缓存命中的次数;DIRTIES
:表示新增到缓存中的脏页数;BUFFERS_MB
:表示 Buffers
的大小,以 MB 为单位;CACHED_MB
:表示 Cache
的大小,以 MB 为单位。$ cachetop
11:58:50 Buffers MB: 258 / Cached MB: 347 / Sort: HITS / Order: ascending
PID UID CMD HITS MISSES DIRTIES READ_HIT% WRITE_HIT%
13029 root python 1 0 0 100.0% 0.0%
它的输出跟 top
类似,默认按照缓存的命中次数(HITS)排序,展示了每个进程的缓存命中情况。具体到每一个指标,这里的 HITS
、MISSES
和 DIRTIES
,跟 cachestat
里的含义一样,分别代表间隔时间内的缓存命中次数、未命中次数以及新增到缓存中的脏页数。
首先,你最容易想到的是 系统内存使用情况 ,比如已用内存、剩余内存、共享内存、可用内存、缓存和缓冲区的用量等。
tmpfs
(内存的文件系统 )实现的所以它的大小也就是 tmpfs
使用的内存大小。tmpfs
其实也是一种特殊的缓存;Slab
分配器中的可回收内存;第二类很容易想到的,应该是 进程内存使用情况,比如进程的虚拟内存、常驻内存、共享内存以及Swap
内存等。
Swap
内存,是指通过 Swap
换出到磁盘的内存。第三类 缺页异常 ,系统调用内存分配请求后,并不会立刻为其分配物理内存,而是在请求首次访问时,通过缺页异常来分配。缺页异常又分为下面两种场景。
I/O
介入(比如 Swap
)时,被称为主缺页异常。第四类重要指标就是** Swap的使用情况** ,比如 Swap
的已用空间、剩余空间、换入速度和换出速度等。
常见的优化思路有这么几种。
Swap
。如果必须开启 Swap
,降低 swappiness
的值,减少内存回收时 Swap
的使用倾向。cgroups
等方式限制进程的内存使用情况。这样可以确保系统内存不会被异常进程耗尽。/proc/pid/oom_adj
,调整核心应用的 oom_score
。这样,可以保证即使内存紧张,核心应用也不会被 OOM
杀死。