Jusene's Blog

【读书笔记】Linux性能优化 内存篇

字数统计: 3.1k阅读时长: 11 min
2019/12/14 Share

内存性能指标

系统内存使用情况:

1
~]# free
2
              total        used        free      shared  buff/cache   available
3
Mem:        2031744       98176     1826192        8784      107376     1800144
4
Swap:       2097148           0     2097148
  • used: 已用内存
  • free: 剩余内存
  • shared: 共享内存, 共享内存是通过tmpfs实现的,它的大小就是tmpfs使用的内存大小
  • available: 可用内存,是新进程可以使用的最大内存,包括剩余内存和还未使用的内存
  • buffer/cache: 缓存包括两部分,一部分是磁盘读取文件的页缓存,用来缓存从磁盘读取的数据,加速以后再次访问速度,另一部分是slab分配的可回收缓存;缓冲区是对原始磁盘的临时存储,用来缓存将要写入磁盘的数据,统一优化磁盘写入。

swap的使用情况:

  • 已用空间
  • 剩余空间
1
vmstat 1 1 
2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
3
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
4
 1  0      0 1815348   2108 111872    0    0     1     0   11   11  0  0 100  0  0
  • si: 换入,每秒从磁盘读入虚拟内存的大小,这个值大于0,表示物理内存不够或者内存泄漏,需要排查内存问题
  • so: 换出,每秒从内存写入磁盘的大小,这个值大于0,表示物理内存不够用,需要排查内存问题。

进程内存使用情况:

1
~] # top
2
3
 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
4
    1 root      20   0  128032   7996   5556 S   0.0  0.4   0:01.03 systemd
5
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kthreadd
  • VIRI: 虚拟内存,包括了进程的代码段、数据段、共享内存、已经申请的堆内存和已经换出的内存等,已经申请的内存,即使还未分配物理内存,也算做虚拟内存
  • RSS: 常驻内存,是进程实际使用的物理内存,不包括swap和共享内存
  • SHR: 共享内存,包括与其他进程共同使用的真实共享内存,包括加载的动态链接库以及程序的代码段
  • %MEM: 进程使用物理内存占系统内存的百分比

内存

内存通常被认为的是物理内存,但是只有内核才可以直接访问物理内存,进程需要访问内存,linux内核需要给每个进程都提供一个独立的虚拟地址空间,访问的是虚拟内存。

虚拟内存空间的内部被划分为内核空间和用户空间:

  • 进程在用户态,只能访问用户空间内存
  • 进程进入内核态才能访问内核空间内存
  • 每个进程都包含内核空间,但这些内核空间都关联相同的物理内存

内存映射

将虚拟内存地址映射到物理内存地址,为了完成内存映射,内核每个进程都维护了一张页表,记录虚拟地址和物理地址的映射关系,页表实际存储在CPU的内存管理单元MMU,这样处理器就可以直接通过硬件找出要访问的内存。

MMU并不是以字节为单位管理内存,而是页,通常是4KB大小,但是这样会导致一个问题,整个页表变的非常大,为了解决这个页表项过多,Linux提供两种机制:多级页表和大页

  • 多级页表

多级页表就是把内存分成区块来管理,将原来的映射关系改成区块索引和区块内的偏移,Linux用四级页表管理内存页:

  • PGD: 全局页目录

  • PUD: 上层页目录

  • PMD: 中间页目录

  • PTE: 直接页表

  • Offset: 页

  • 大页

就是比普通页更大的内存块,常见的大小2MB和1GB, 大页通常用在使用大量内存的进程上

缺页异常

在内存分配的原理中,系统调用内存分配请求后,并不会立刻为其分配物理内存,而是首次请求时,通过缺页异常来分配,缺页异常分为两种场景:

  • 可以直接从物理内存中分配时,被称为次缺页异常
  • 需要磁盘I/O介入,被称为主缺页异常

系统回收内存

  • 回收缓存,比如LRU算法,回收最近使用最少的内存页面
  • 回收不常使用的内存,把不常用的内存通过交换分区直接写入到磁盘中
  • 杀死进程,内存紧张系统还会通过OOM

OOM是内核的一种保护机制,使用oom_score为每个进程的内存使用情况进行评分

一个进程消耗的内存越大,oom_score就越大
一个进程运行的CPU越多,oom_score就越小

可以通过/proc/{pid}/oom_adj [-17, 15],数值越大,表示数值越大,越容易被oom杀死,-17表示禁止

Buffer/Cache

Buffer是对磁盘数据的缓存,而cache是对文件数据的缓存,既会用在读请求中,也会在写请求中。

  • 从写的角度来说,不仅可以优化磁盘和文件的写入,对应用程序也有好处,应用程序可以在数据真正落盘前,就返回去做其他工作。
  • 从读的角度来说,既可以加速读取那些需要频繁访问的数据,也降低了频繁I/O对磁盘的压力

/proc/<pid>/smaps

  • Rss: 实际使用物理内存(包含共享库占用的内存)
  • Pss: 实际使用的物理内存(按比例包含共享库占用的内存)

所有如果要计算一个进程实际使用的物理内存:

1
awk '/Pss:/{sum+=$2}END{print sum}' /proc/<pid>/smaps

要计算实际使用的物理内存大小:

1
grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END{printf "%d kb\n", total}'

/proc/sys/vm/drop_caches

  • 1 清空页缓存
  • 2 清空inode和目录树缓存
  • 3 清空所有缓存

内存泄漏

内存泄漏危害极大,这些忘记释放的内存,不仅应用程序自己不能访问,系统也不能把它们再次分配给其他应用,内存泄漏不断累积,甚至会耗尽系统内存。

memleak是bcc软件包的一个工具,而bcc工具需要内核4.1或者更高,centos需要升级内核:

1
# 升级系统
2
yum update -y
3
# 安装elrepo
4
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
5
rpm -Uvh https://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
6
# 安装新内核
7
yum remove -y kernel-headers kernel-tools kernel-tools-libs
8
yum --enablerepo="elrepo-kernel" install -y kernel-ml kernel-ml-devel kernel-ml-headers kernel-ml-tools kernel-ml-tools-libs kernel-ml-tools-libs-devel
9
# 更新grub后重启
10
grub2-mkconfig -o /boot/grub2/grub.cfg
11
grub2-set-default 0
12
reboot
1
安装bcc
2
yum install -y bcc-tools
3
export PATH=$PATH:/usr/share/bcc/tools

memleak:

  • -a表示显示每个内存分配请求的大小以及地址
  • -p指定案例应用的pid
1
memleak -a -p <pid>

SWAP

内存回收

内存回收,也就是系统释放可以回收的内存,比如缓存和缓冲区,就属于可回收内存,在内存管理中,通常被叫做文件页,大部分文件页都是可以直接回收的。

那些被程序修改过,并且暂时还没写入磁盘的数据(脏页)写入磁盘的两种方式:

  • 可以在应用程序中,通过系统调用fsync,把脏页同步到磁盘
  • 也可以交给系统,由内核线程pdflush负责脏页刷新

应用程序动态分配的堆内存,就是内存管理的匿名页,如果这些内存分配后很少被访问,可以把它们暂时先存在磁盘中,释放内存给其他进程。
swap机制,swap把不常访问的内存先写到磁盘中,然后释放这些内存,给其他进程使用,再次访问这些内存时,重新从磁盘读入内存。

新的内存分配请求,但是剩余内存不足,系统需要回收一部分内存,这个过程通常被称为直接内存回收。

除了直接内存回收,还有内核线程来定期回收内存,kswapd0,kswapd0定义三个内存阈值:

  • 页最小阈值
  • 页低阈值
  • 页高阈值
1
- page > 页高阈值: 内存充足
2
- 页高阈值 > page > 页低阈值: 内存分配正常
3
- 页低阈值 > page > 页最小阈值: 内存压力巨大
4
- 页最小阈值 > page: 内存基本耗尽,仅内核可分配内存

剩余内存小于低阈值,就会触发内存回收

1
/proc/sys/vm/min_free_kbytes  # 页最小阈值
2
3
pages_low = pages_min * 5/4
4
pages_high = pages_min * 3/2
  • 对文件页的回收,当然就是直接回收缓存,或者把脏页写到磁盘后再回收
  • 对匿名页的回收,通过swap机制,把它们写入磁盘后在释放内存

linux提供了一个/proc/sys/vm/swappiness选项,用来调整使用swap的积极程度

swappiness的范围0-100,数值越大越积极使用swap,更倾向于回收匿名页;数值越小,越消极使用swap,也就是更倾向于回收文件页。

即使设置0,剩余内存+文件页 小于 高阈值,还是会发生swap

性能指标和工具的联系

内存指标 性能工具
系统已用、可用、剩余内存 free vmstat sar /proc/meminfo
进程虚拟内存、常驻内存、共享内存 ps top
进程内存分布 pmap
进程swap换出内存 top /proc/pid/status
进程缺页异常 ps top
系统换页清空 sar
缓存/缓冲区用量 free vmstat sar cachestat
缓存/缓冲区命中率 cachetop
swap已用空间和剩余空间 free sar
swap换入换出 vmstat
内存泄漏检测 memleak valgrind
指定文件的缓存大小 pcstat
性能工具 内存指标
free /proc/meminfo 系统已用、可用、剩余内存以及缓存和缓冲区的使用量
top ps 进程虚拟、常驻、共享内存以及缺页异常
vmstat 系统剩余内存、缓存、缓冲区、换入换出
sar 系统内存换页情况、内存使用率、缓存和缓冲区用量以及swap使用情况
cachestat 系统缓存和缓冲区的命中率
cachetop 进程缓存和缓冲区的命中率
slabtop 系统slab缓存的使用情况
/proc/pid/status 进程swap内存等
/proc/pid/smaps pmap 进程地址空间和内存状态
valgrind 进程内存错误检查器,检测内存初始化,泄漏,越界访问等各种错误
memleak 内存泄漏问题
pcstat 查看指定文件的缓存情况

内存问题解决思路

为了快速定位内存问题,通常会先运行几个覆盖面比较大的性能工具,比如free、top、vmstat、pidstat

  1. 先用free或top,查看系统的整体的内存使用情况
  2. 在用vmstat和pidstat,查看一段时间的趋势,从而判断出内存问题类型
  3. 在进行详细的分析,比如内存分配分析、缓存/缓冲区分析、具体进程的内存使用分析等

第一阶段:

free

  • 系统已用内存
  • 系统剩余内存
  • 系统可用内存
  • 缓存/缓冲区
  • swap已用空间
  • swap剩余空间

确认系统的整体内存使用情况

第二阶段:

vmstat/sar查看趋势

  • 系统剩余内存
  • 缓冲区
  • 缓存
  • swap换入
  • swap换出
  • 缺页异常

确定内存瓶颈

第三阶段:

确定内存问题

  • 系统剩余内存不足
  • 系统可用内存不足
  • 缓存/缓冲区过多
  • 内存泄漏
  • 大量缺页异常
  • 使用了swap异常
  • swap剩余空间过小

第四阶段:

  • 内存分配分析

  • memleak

  • strace

  • valgrind

  • slabtop

  • /proc/buddyinfo

  • 进程内存分析

  • pidstat/top

  • pmap

  • /proc/pid/status

  • /proc/pid/smaps

  • 缓存/缓冲区分析

  • cachetop

  • slabtop

  • cachestat

  • pcstat

内存优化思路

  1. 最好禁止swap,如必须开启swap。降低swappiness的值
  2. 减少内存的动态分配,可以使用内存池、大页等
  3. 尽量使用缓存和缓冲区来访问数据
  4. 使用cgroup等方式限制进程的内存使用情况
  5. 通过/proc/pid/oom_adj,调整核心应用的oom_score
CATALOG
  1. 1. 内存性能指标
  2. 2. 内存
    1. 2.1. 内存映射
    2. 2.2.
    3. 2.3. 缺页异常
    4. 2.4. 系统回收内存
  3. 3. Buffer/Cache
  4. 4. 内存泄漏
  5. 5. SWAP
    1. 5.1. 内存回收
  6. 6. 性能指标和工具的联系
  7. 7. 内存问题解决思路
  8. 8. 内存优化思路