Linux性能优化实战-内存篇

linux性能优化之内存

一、Linux内存是怎么工作的?
  • 什么是内存映射?

    • 虚拟内存地址到物理内存地址的映射机制

      • 页表 -> 存储在cpu的内存管理单元MMU/TLB,通过MMU通过TLB缓存页表,页表页大小4kb一页

      • 如果页表使用线性结构,整个虚拟地址空间需要占用大量的页表项 * 如何避免页表项过多 * 多级页表 -> 本质上是个多叉树 * 仅保留使用中的页,因此可以节省多数未使用的页表项 * 一级 -> 二级 -> 三级 -> 偏移量 => 物理地址 * 转换成线性结构: 1级 * 2级 * 3级 * 大页 -> 2MB、1GB

    • 什么是虚拟内存?

      • 进程独享的内存地址空间

        • 内核空间 -> 内核态可访问

        • 用户空间 -> 用户态可访问

      • 地址空间 -> 32位/64位范围

        • 虚拟内存 >> 物理内存

        • 分段

          • 只读 -> 常量,代码

          • 数据 -> 全局变量

          • 堆 -> 动态分配内存

          • 文件映射 -> 动态库,共享内存(进程共享)

          • 栈 -> 局部变量,函数调用上下文

  • 如何分配和回收内存?

    • 动态分配和释放

      • 堆 -> 适合小对象

        • brk()/free() 系统调用

        • 可重复使用,释放后不会立即归还系统

        • 容易导致内存碎片

      • 文件映射 -> 适合大对象

        • mmap()/unmap() 系统调用

        • 每次调用都会触发缺页异常

        • 释放后立刻归还系统

      • 物理内存分配管理机制

        • 页 -> 按页分配

        • SLAB -> 适合小对象

        • 其他

    • 系统回收

      • 回收缓存(LRU)

      • swap -> 交换不常用的内存到磁盘

        • 可能导致性能严重下降,例如ES等对性能要求严格的服务会禁用swap

      • OOM -> oom_score 打分机制

        • 内存高,cpu占用低的进程oom_score较大

        • 手动打分 -> echo -16 > /proc/$(pidof sshd)/oom_adj

        • 查看OOM日志 -> dmesg |grep -E ‘kill|oom|out of memory‘

  • 内存指标

    • free

      • total -> 总内存

      • used -> 已使用内存,包括share

      • free -> 未使用内存(排除了buff/cache/share

      • share -> 共享内存(共享 + 程序代码段 + 动态链接库)

      • buff/cache -> buffers(磁盘缓存) + cached(页缓存)+ SReclaim(可回收Slab)

        • slab -> 管理内核中的小块内存

      • available -> 可用内存

    • top

      • VIRT -> 虚拟内存(申请过的,即使没有实际分配物理内存)

        • 内核线程该指标=0

      • RES -> 常驻内存 (实际使用的物理内存,不包括swap)

      • SHR -> 共享内存(共享 + 程序代码段 + 动态链接库)

      • %MEM -> 物理内存占用 / 总内存

二、怎样理解内存中的Buffer和Cache?

Buffers是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据(通常不会特别大)。Cached是从磁盘读取文件的页缓存,用来缓存从文件中读取的数据。Slab包括可回收和不可回收两部分。

三、内存泄漏,如何定位和处理?

虚拟内存分布从低到高分别是只读段,数据段,堆,内存映射段,栈五部分。其中会导致内存泄漏的是:

堆: 由应用程序自己来分配和管理,除非程序退出这些堆内存不会被系统自动释放。 内存映射段:包括动态链接库和共享内存,其*享内存由程序自动分配和管理 内存泄漏的危害比较大,这些忘记释放的内存,不仅应用程序自己不能访问,系统也不能把它们再次分配给其他应用。 内存泄漏不断累积甚至会耗尽系统内存。

实验 如何检测内存泄漏 预先安装systat,docker,bcc

sudo docker run --name=app -itd feisky/app:mem-leak sudo docker logs app vmstat 3 可以看到free在不断下降,buffer和cache基本保持不变。说明系统的内存一致在升高。但并不能说明存在内存泄漏。此时可以通过memleak工具来跟踪系统或进程的内存分配/释放请求。

/usr/share/bcc/tools/memleak -a -p $(pidof app) 从memleak输出可以看到,应用在不停地分配内存,并且这些分配的地址并没有被回收。通过调用栈看到是fibonacci函数分配的内存没有释放。定位到源码后查看源码来修复增加内存释放函数即可。

 

内存性能指标

系统内存指标

  • 已用内存/剩余内存

  • 共享内存 (tmpfs实现)

  • 可用内存: 包括剩余内存和可回收内存

  • 缓存:磁盘读取文件的页缓存,slab分配器中的可回收部分

  • 缓冲区: 原始磁盘块的临时存储,缓存将要写入磁盘的数据

进程内存指标

  • 虚拟内存: 5大部分

  • 常驻内存: 进程实际使用的物理内存,不包括Swap和共享内存

  • 共享内存: 与其他进程共享的内存,以及动态链接库和程序的代码段

  • Swap内存: 通过Swap换出到磁盘的内存

缺页异常

  • 可以直接从物理内存中分配,次缺页异常

  • 需要磁盘IO介入(如Swap),主缺页异常。 此时内存访问会慢很多

内存性能工具

根据不同的性能指标来找合适的工具: 

Linux性能优化实战-内存篇

内存分析工具包含的性能指标: 

Linux性能优化实战-内存篇

如何迅速分析内存的性能瓶颈

通常先运行几个覆盖面比较大的性能工具,如free,top,vmstat,pidstat等

  • 先用free和top查看系统整体内存使用情况

  • 再用vmstat和pidstat,查看一段时间的趋势,从而判断内存问题的类型

  • 最后进行详细分析,比如内存分配分析,缓存/缓冲区分析,具体进程的内存使用分析等

常见的优化思路:

  • 最好禁止Swap,若必须开启则尽量降低swappiness的值

  • 减少内存的动态分配,如可以用内存池,HugePage等

  • 尽量使用缓存和缓冲区来访问数据。如用堆栈明确声明内存空间来存储需要缓存的数据,或者用Redis外部缓存组件来优化数据的访问

  • cgroups等方式来限制进程的内存使用情况,确保系统内存不被异常进程耗尽

  • /proc/pid/oom_adj调整核心应用的oom_score,保证即使内存紧张核心应用也不会被OOM杀死

Linux性能优化实战-内存篇

上一篇:linux常见的发行版本


下一篇:结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程