查看系统内存使用情况
使用 free , 单位为 B1
2
3
4root@imwl01:~# free
total used free shared buff/cache available
Mem: 4001696 514836 2834488 1572 652372 3230228
Swap: 0 0 0
- total : 总内存大小
- used : 已使用内存的大小,包含了共享内存
- free : 未使用内存的大小
- shared : 共享内存的大小 tmpfs ,一种特殊的缓存
- buff/cache : 缓存和缓冲区的大小 Buffer 是对磁盘数据的缓存,而 Cache 是文件数据的缓存,它们既会用在读请求中,也会用在写请求中
- available : 新进程可用内存的大小,包含 未使用 + 可回收
读写普通文件时,I/O 请求会首先经过文件系统,然后由文件系统负责,来与磁盘进行交互 cache。而在读写块设备文件时,会跳过文件系统,直接与磁盘交互,也就是所谓的”裸 I/O” buff
使用 top ,按 M 按内存排序
1 | root@imwl01:~# top |
- VIRT : 进程虚拟内存的大小,只要是进程申请过的内存,即便还没有真正分配物理内存,也会计算在内。
- RES : 常驻内存的大小,也就是进程实际使用的物理内存大小,但不包括 Swap 和共享内存。
- SHR : 共享内存的大小,比如与其他进程共同使用的共享内存、加载的动态链接库以及程序的代码段等。
- %MEM : 进程使用物理内存占系统总内存的百分比。
查看所有进程使用的内存总量
Pss: 把共享内存平分到各个进程后,再加上进程本身的非共享内存大小的和1
2# 使用 grep 查找 Pss 指标后,再用 awk 计算累加值
$ grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {printf "%d kB\n", total }'
虚拟内存
内存是稀缺的,随着应用使用内存也在膨胀。当程序越来复杂,进程对内存的需求会越来越大。
虚拟化技术是为了解决内存不够用的问题
虚拟化技术中,操作系统设计了虚拟内存(理论上可以无限大的空间),受限于 CPU 的处理能力,通常 64bit CPU,就是 2**64 个地址
虚拟化技术中,应用使用的是虚拟内存,操作系统管理虚拟内存和真实内存之间的映射。操作系统将虚拟内存分成整齐小块,每个小块称为一个页(Page)。之所以这样做,原因主要有以下两个方面。
一方面应用使用内存是以页为单位,整齐的页能够避免内存碎片问题。
另一方面,每个应用都有高频使用的数据和低频使用的数据。这样做,操作系统就不必从应用角度去思考哪个进程是高频的,仅需思考哪些页被高频使用、哪些页被低频使用。如果是低频使用,就将它们保存到硬盘上;如果是高频使用,就让它们保留在真实内存中。
如果一个应用需要非常大的内存,应用申请的是虚拟内存中的很多个页,真实内存不一定需要够用
大页 : 比普通页(4KB)更大的内存块,常见的大小有 2MB 和 1GB。大页通常用在使用大量内存的进程上,比如 Oracle、DPDK 等
内存映射
- 物理内存 : 只有内核才可以直接访问
- 虚拟内存 : Linux 内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的
内存映射 : 其实就是将虚拟内存地址映射到物理内存地址

虚拟内存空间分布

内存分配与回收
分配
对小块内存(小于 128K),C 标准库使用 brk() 来分配,也就是通过移动堆顶的位置来分配内存。这些内存释放后并不会立刻归还系统,而是被缓存起来,这样就可以重复使用。
而大块内存(大于 128K),则直接使用内存映射 mmap() 来分配,也就是在文件映射段找一块空闲内存分配出去。
回收
回收缓存,比如使用 LRU(Least Recently Used)算法,回收最近使用最少的内存页面;
回收不常访问的内存,把不常用的内存通过交换分区直接写到磁盘中
杀死进程,内存紧张时系统还会通过 OOM(Out of Memory),直接杀掉占用大量内存的进程。
内存泄漏
只读段,包括程序的代码和常量,由于是只读的,不会再去分配新的内存,所以也不会产生内存泄漏。
数据段,包括全局变量和静态变量,这些变量在定义时就已经确定了大小,所以也不会产生内存泄漏。
堆,栈, 内存映射段,包括动态链接库和共享内存,其中共享内存由程序动态分配和管理。所以,如果程序在分配后忘了回收,就会导致泄漏问题
Swap
Swap 技术允许一部分进程使用内存,不使用内存的进程数据先保存在磁盘上。注意,这里提到的数据,是完整的进程数据,包括正文段(程序指令)、数据段、堆栈段等。轮到某个进程执行的时候,尝试为这个进程在内存中找到一块空闲的区域。如果空间不足,就考虑把没有在执行的进程交换( Swap )到磁盘上,把空间腾挪出来给需要的进程。
Swap 技术在性能上存在着碎片、频繁切换等明显劣势。使用 Swap 技术,程序员需要清楚地知道自己的应用用多少内存,并且小心翼翼地使用内存,避免需要重新申请,或者研发不断扩容的算法。一般不推荐使用 swap
- 换出 : 把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存。
- 换入 : 进程再次访问这些内存的时候,把它们从磁盘读到内存中来。
swap 类型:
- swap 分区
- swap 文件
有关设置
- /proc/sys/vm/min_free_kbytes :一旦剩余内存小于页低阈值,就会触发内存的回收
- /proc/sys/vm/swappiness :调整文件页和匿名页的回收倾向 0-100,越低越不使用 swap ,为 0,当剩余内存 + 文件页小于页高阈值时,还是会发生 Swap
分析缓存命中率
缓存命中率 : 通过缓存获取数据的请求次数,占所有数据请求次数的百分比
因为 内存 的 数据读写比 磁盘读写速度快的多,所以可以依赖缓存提升速度
总结
内存优化常见思路
- 禁止 Swap。如果必须开启 Swap,降低 swappiness 的值,减少内存回收时 Swap 的使用倾向。
- 减少内存的动态分配。比如,可以使用内存池、大页(HugePage)等。
- 尽量使用缓存和缓冲区来访问数据。比如,可以使用堆栈明确声明内存空间,来存储需要缓存的数据;或者用 Redis 这类的外部缓存组件,优化数据的访问。
- 使用 cgroups 等方式限制进程的内存使用情况。这样,可以确保系统内存不会被异常进程耗尽。
- 通过 /proc/pid/oom_adj ,调整核心应用的 oom_score。oom_score 越小,进程就越不容易被系统杀死
常用思路和工具


