最近在复习总结linux内存优化相关知识,撰写此文,本文分别从以下几个部分展开,内存瓶颈有什么特征?如何去优化?以及怎么去衡量一个系统的内存情况,有什么指标和工具?内存泄漏怎么发现,内存回收相关知识,另外,还给出了一些常见的内存调优方法和内核调整参数。
另外,此文是我总结的思维导图导出后调整格式发布的,由于思维导图太大了,放图片的话会模糊,关于思维导图,如需要的话可私聊。
目录
/proc/sys/vm/nr_pdflush_threads
-
内存瓶颈
-
特征
-
可用内存(available)持续减少,系统进程中kswapd进程频繁出现,同时Swap交换空间占用率持续增高
-
-
原因
- 一种是应用程序bug导致内存资源耗尽;另一种是确实内存资源不足
- 解决方法:针对第一种情况,需要从应用程序角度来排查问题。如果出现第二种情况,就要考虑增加内存资源了
- 一种是应用程序bug导致内存资源耗尽;另一种是确实内存资源不足
-
性能优化措施
- 关掉操作系统上用不到的服务
- 增加内存
- 需要注意的是在内存充足的情况下,如果再增加内存的话,内存瓶颈就会转为CPU瓶颈,因为CPU的运行速度也就是每秒内执行的机器指令数量是有限的
- 通过cgroups等措施限制进程内存使用
- 非必要不使用swap,如果必须用swap,调整Swapiness,降低内存回收时使用swap的机率
- 临时调整
- sysctl vm.swapiness=10 临时设置swapiness为10
- 永久调整
- vim /etc.sysctl.conf vm.swapiness=1
- 临时调整
- 减少内存的动态分配
- 使用bigpages、hugetlb和共享内存调优。
- 优化操作系统虚拟内存参数(如/proc/sys/vm)。
- 尽量使用缓存和缓冲区来访问数据
- 使用堆栈时,明确声明其内存空间来存储需要缓存的数据
- 用redis这类的外部缓存组件,来优化数据访问
- 保护核心应用不被OOM杀死
- 通过/proc/pid/oom_abj调整oom_score
- 保证内存紧张时,核心应用也不会被OOM KILL
-
-
内存指标
-
物理内存和逻辑内存
- 物理内存就是系统硬件提供的内存大小,是真正的内存。
- 逻辑内存就是为了满足物理内存的不足而提出的策略,它是利用磁盘空间虚拟出的一块逻辑内存区域,用作逻辑内存的磁盘空间被称为交换空间(Swap Space)
-
进程内存
- 虚拟内存VSS
- 已经申请,但是并未分配物理内存,也称作虚拟内存
- 包括了进程代码段、数据段、共享内存、已经申请的堆内存和已经换出的内存
- 共享内存
- 包括与其他进程共同使用的真实的共享内存
- 包括了加载的动态链接库以及程序的代码段等
- 常驻内存RSS
- 是进程实际使用的物理内存,不包括共享内存和swap
- 系统内存使用率是常驻内存占整个系统的总内存百分比
- 独占内存USS
- USS(Unique Set Size):进程独自占用的内存,它只计算了进程独自占用的内存大小,不包含任何共享的部分。默认是USS
- 按比例分配共享内存后的物理内存PSS(Proportional Set Size)
- 所有进程的PSS之和就是系统的内存使用量
- 它将共享内存的大小进行平均后,再分摊到各进程上去
- swap内存
- swap是linux中时间换空间的一种操作,当swap空间占用多,频繁换入换出,会导致系统很卡顿
- swap是指通过 Swap 换出到磁盘的内存
- 虚拟内存VSS
-
系统内存
- 已用内存
- 剩余内存
- 共享内存
- 是通过 tmpfs 实现
- 大小也就是 tmpfs 使用的内存大小
- 可用内存
- 包括剩余内存和可回收缓存
- 缓存
- 磁盘读取文件的页缓存
- Slab 分配器中的可回收内存
- 缓冲区
- 缓存了将要写入磁盘的数据
- 内核就可以把分散的写集中起来,统一优化磁盘写入
- 缺页异常
- 次缺页异常
- 直接从系统物理内存分配
- 主缺页异常
- 从swap等需要磁盘IO介入的部分 分配
- 次缺页异常
- sawp使用情况
- 换入速度
- 每秒换入的内存大小
- 换出速度
- 每秒换出的内存大小
- 已用空间
- 剩余空间
- 换入速度
-
-
内存调优常用工具
- free
- smem
- 可以查看每个进程的物理内存使用情况
- vmstat
- 可以观察内存的变化情况
- pmap
- 查看进程的内存分布
- sar
- cachetop
- pmap
- cachestat
-
内存泄露
-
原因
- 使用完内存空间后忘记释放了
- 堆
- 当我们不知道使用的数据大小时,会使用malloc()在程序中动态分配内存
- 系统就会从内存空间的堆中分配内存
- 使用后忘记释放会出现内存泄漏风险
- 栈
- 分配的局部变量
- 由系统进行内存管理,不会出现内存泄漏问题
- 其他内存段
- 只读段
- 包括程序的代码和常量,只读的不会分配新内存,所以不会产生内存泄漏
- 数据段
- 包括全局变量和静态变量
- 在定义时候已经定好了大小,也不会产生memoryleak
- 内存映射段
- 包括动态链接库和共享内存
- 共享内存由程序动态分配和管理,因此在程序使用后忘记回收,就会导致和堆内存类似的泄露问题
- 只读段
- 堆
- 使用完内存空间后忘记释放了
-
影响
- 泄露的内存不断累积,造成内存浪费,系统无法为新进程分配内存,swap交换分区如打开的话会进行回收,并且剩余内存不多的话系统会开启内存回收,这将进一步导致IO问题
-
如何定位
- vmstat查看内存变化趋势
- memleak查看内存分配,从而定位是哪个程序内存异常
- 进入程序内部,查看代码,是否没有释放内存
-
生产环境
- 生产环境中除了一些程序错误导致内存迅速发生异常(这类用工具可以很容易发现),还可能会出现一些慢性积累过程,比如说一些缓慢的内存增长,用vmstat几乎看不到变化趋势,但是长此以往,对内存的消耗很大,这种情况下可以上监控,监控可以看到内存指标增长的趋势
-
-
为什么剩余内存很多的情况下,也会发生 Swap
- 处理器架构NUMA导致的
- NUMA is(Non-Uniform Memory
Access) - 在 NUMA 架构下,多个处理器被划分到不同 Node 上,且每个 Node 都拥有自己的本地内存空间。
- 一个node内部的内存空间可分为不同的内存域(Zone)
- 直接内存访问区(DMA)
- 普通内存区(NORMAL)
- 伪内存区(MOVABLE)
- ....
- numactl --hardware可以查看处理器在不同node上的分布情况
- 当某一个node内存不足时,可以从其他node回收内存
- /proc/sys/vm/zone_reclaim_mode 可以调节回收方式
- 默认是0,可以从其他node找空闲内存或从本地回收内存
- 1表示内存回收只会发生在本地节点内
- 2表示可以回写Cache中脏数据到磁盘来回收内存
- 4表示可以使用swap回收内存
- 一个node内部的内存空间可分为不同的内存域(Zone)
- NUMA is(Non-Uniform Memory
- 处理器架构NUMA导致的
-
内存回收
-
匿名页(动态分配的堆内存)
- 不可直接回收
- swap将匿名页先写到磁盘,然后再回收内存
- 当需要访问这部分匿名页时,再从swap中读取即可
- 不可直接回收
-
文件页
- 可直接回收
- 文件页(包括缓存和缓冲区)
- 不可直接回收
- 脏页
- 需要先写入磁盘再进行回收
- 通过系统调用fsnc同步写入
- 系统的内核线程pdflush刷新脏页
- 需要先写入磁盘再进行回收
- 脏页
- 可直接回收
-
如何解决回收内存会产生磁盘IO这种影响?
- 调整swapiness
- 回收匿名页(swapiness较大)一定会发生换入换出行为,产生磁盘IO,因此可以尝试降低 swappiness 的值,减少内存回收时 Swap 的使用倾向
- 回收文件页(swapiness较小)在回收干净页时不会发生磁盘写入行为,也就没有磁盘IO
- 尽早触发系统内存回收
- kswapd是后台内存回收机制,它是异步的,不会阻塞进程。
- 除非有必要使用swap,禁用swap
- 响应延迟敏感的应用,如果它们可能在开启 Swap 的服务器中运行,你还可以用库函数mlock() 或者 mlockall() 锁定内存,阻止它们的内存换出
- 调整swapiness
-
如何调整内存回收时回收的是哪种类型内存呢?
- /proc/sys/vm/swappiness
- swappiness越小,越倾向于回收文件页
- swapiness越大,越倾向于回收匿名页
- 范围是0-100,表示的是一种倾向,当为0时也不是表示回收内存完全不用匿名页,当空心内存加上文件页仍小于pages_low,还是会用Swap
- /proc/sys/vm/swappiness
-
内核里有一个线程kswapd0会定期扫描,进行内存回收
- 一旦剩余内存小于pages_min即页低阈值,就会触发内存的回收
- 页低阈值可以通过/proc/sys/vm/min_free_kbytes来设置
-
-
内核内存参数优化
-
/proc/sys/vm/swappiness
- 表示使用Swap分区的概率
- 默认60,这是一个相对中和的配置,所以系统会根据实际运行情况,选择合适的回收类型
- 在一些数据库服务器上,如Redis、HBase机器上,应该设置0~10之间,表示最大限度使用物理内存。
- swappiness=100时表示积极使用Swap分区,并且把内存上的数据及时搬运到Swap空间里面
- swappiness=0时表示最大限度使用物理内存,然后才是Swap空间
-
/proc/sys/vm/panic_on_oom
- 表示内存不够时内核是否直接panic
- 默认值为0,表示当内存耗尽时,内核会触发OOM killer杀掉最耗内存的进程。
- 根据 oom_score来杀死,在/proc/${pid}/oom_score 越高的分数意味着越可能被kill,这个数值是根据oom_adj运算后的结果,是oom_killer的主要参考。
- /proc/[pid]/oom_adj 取值-17~15 越高的权重,意味着更可能被oom killer选中,-17表示禁止被kill掉。
- 设置为1表示在OOM时系统会panic(恐慌)
- 为了不让系统自动kill掉进程,需要设置此值为1。
-
/proc/sys/vm/nr_pdflush_threads
- 表示当前正在运行的pdflush进程数量,在I/O负载高的情况下,内核会自动增加更多的pdflush进程。
-
/proc/sys/vm/min_free_kbytes
- 表示强制Linux VM最低保留多少空闲内存
- 默认值为90112(88M物理内存,CentOS7版本),保持默认即可。
- pages_low = pages_min*5/4
pages_high = pages_min*3/2
-
关于脏数据处理方法的一些内核参数
- /proc/sys/vm/dirty_writeback_centisecs
- 控制内核的脏数据刷新进程pdflush的运行间隔
- 单位是(1/100)s。默认值是500,也就是5s
- 如果系统是持续地写入动作,那么建议降低这个数值,这样可以把尖峰的写操作削平成多次写操作;
- 如果系统是短期地尖峰式的写操作,并且写入数据不大且内存又比较富裕,那么应该增大此数值。
- /proc/sys/vm/dirty_ratio
- 参数指定了当文件系统缓存脏数据数量达到系统内存百分之多少时,系统不得不开始处理缓存脏页
- 如果触发了这个设置,那么新的I/O请求将会被阻挡,直到脏数据被写进磁盘。这是造成I/O卡顿的重要原因,但这也是保证内存中不会存在过量脏数据的保护机制
- 在磁盘写入不是很频繁的场景,适当增大此值
- 如果是持续、恒定的写入场合,应该降低其数值。
- /proc/sys/vm/dirty_writeback_centisecs
-