linux性能优化-内存

内存映射

pc里面的内存条,称之为 物理内存, 系统里面每个进程启动的时候 内核会分配一个独有的连续的虚拟内存, 所以系统并不直接读取物理内存, 只有实际使用的虚拟内存才会给分配物理内存, 并且分配了物理内存之后通过内存映射来管理,
内存映射: 虚拟内存映射到物理内存,内核为内阁进程都维护了一张页表,记录虚拟内存和物理内存的映射地址,

内存映射

当进程访问的虚拟内存在页表中查不到的时候, 系统会产生一个缺页异常, 进入内核空间分配物理内存,更新进程页表,最后返回用户空间,恢复进程的运行.

内存回收

内存使用情况

top命令下的某参数说明

  • VIRT: 进程虚拟内存的大小,只要是进程申请过的内存,即便还没有真正的分配物理内存,也会计算在内
  • RES: 常驻内存的大小,也就是进程实际使用过程中使用的物理内存的大小,但不包括 swap和共享内存
  • SHR: 是共享内存的大小,比如与其他进程共同使用的共享内存,加载的动态链接库以及程序的代码段等

注意两点:

  • 一 : 虚拟内存通常并不会全部分配物理内存,从top命令可以看出,每个进程的虚拟内存都比常驻内存要大的多
  • 二 : 共享内存SHR 并不一定是共享的. 比方说,程序的代码段 非共享的动态链接库,也都算在SHR里面,

buffer 和 cache

buffer: 缓冲区
cache: 缓存
对应文件: /proc/meminfo
磁盘数据: 直接读写分区的数据
文件数据: 通过文件系统读写的文件
buffer是对磁盘数据的缓存,cache是对文件数据的缓存, 他们既会用在读请求,也会用在写请求.

回收内存的三种方式

  • 基于LRU算法, 回收缓存
  • 基于swap机制,回收不常访问的匿名页
  • 基于oom机制,杀掉占用大量内存的进程
    前两种方法回收缓存和swap都基于 LRU算法,都是优先回收不常访问的内存,LRU 回收算法其实维护这两个双向链表,active(记录活跃的内存页)和inactive(记录非活跃的内存页),越接近链表尾部,表示内存页越不常访问,回收内存时 根据活跃程度 优先回收不会活跃的进程.
    可以从 /proc/meminfo 中查看他们的大小
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # grep 表示只保留包含 active 的指标(忽略大小写)
    # sort 表示按照字母顺序排序
    $ cat /proc/meminfo | grep -i active | sort
    Active(anon): 167976 kB
    Active(file): 971488 kB
    Active: 1139464 kB
    Inactive(anon): 720 kB
    Inactive(file): 2109536 kB
    Inactive: 2110256 kB
    第三种 oom机制安装 oom_score给进程排序, oom_score越大 进程就越容易被系统杀死
    当系统发现内存不够分配时,会尝试回收内存, 如果回收了文件页和匿名页后 内存够用了 ,然后把内存分配给进程使用就可以了,如果还是不够用 然后在使用 oom机制杀死占用内存量大的进程

查看OOM日志 /var/log/dmesg

1
2
$ dmesg | grep -i "Out of memory"
Out of memory: Kill process 9329 (java) score 321 or sacrifice child

衡量缓存使用的好坏指标?

缓存命中率

  • cachestat : 提供了整个操作系统缓存的读写命中情况
  • cachetop : 提供了每个进程的缓存命中情况

内存泄漏如何处理

内存的分配和回收

  • 一般在局部定义一个变量,比如 int data[64] 就定义了一个可以存储64字节的内存段, 由于是一个局部变量 所以他会在内存空间的栈中分配内存.

    栈内存 由系统自动分配和管理, 所以程序一旦超出 就会被系统自动回收, 就不会产生 内存泄漏的问题.

    但是很多时候我们不知道数据大小, 所以要用到标准函数库 malloc()_, 在程序中动态分配内存, 这时候 系统会从内存空间的堆中分配内存.
    堆内存由应用程序自己分配和管理,除非程序自己退出, 不然这些堆内存不会被自动释放,所以就要用到应用程序明确调用库函数 free()来释放, 如果应用程序没有正确释放,那么就会造成内存泄漏.

  • 内存泄漏危害非常大,如果忘记释放内存,不仅应用自己无法访问,系统也不能把他们的空间自此分配给其他应用, 最终内存耗尽,gg

    但是系统最终可以通过 OOM(out of momeory) 机制杀死进程,但是进程在OOM之前 以及引发了一连串的反应,遭到严重影响,

内存泄漏分析工具

memleak 是bbc软件包中的一个工具, 安装目录 /usr/share/bcc/tools/memleak
分析容器中的应用常见问题: 调用栈不能正常显示,应为容器和外表隔离 所以参考 perf 方法中的案例, 如果单一二进制的应用程序 直接复制出来 运行 也可以

1
2
3
# -a 显示每个内存分配请求的大小以及地址
# -p pid号
memleak -a -p $(pidof 进程)

为何很多程序比如 Hadoop es k8s 等要关闭swap

大多数的java程序都建议关闭swap , 这是应为 jvm 在gc 的时候 需要遍历所有用到的堆内存, 如果这一部分堆内存被swap出去了, 那么遍历的时候就要磁盘IO 严重影响性能

内存性能指标

内存性能指标

分析流程

内存分析流程

内存优化的几种思路

  • 最好禁止 swap 空间, 尤其是java程序,应为jvm在gc的时候会遍历进程的堆内存,如果这一部分内存被交换到swap 那么gc会进行io操作, 影响性能, 如果非要启用swap ,那么尽量减少 swappiness的值, 减少内存回收时swap的使用倾向
  • 减少内存的动态分配,比如, 可以使用内存池,大页(huge page)等.
  • 尽量使用缓存和缓冲来访问数据, 比如 可以使用堆栈明确声明内存空间,来存储需要缓存的数据, 或者使用redis这种第三方缓存组件, 优化数据的访问
  • 使用cgroups等方式限制进程的内存使用情况,这样可以确保系统内存不会被异常进程消耗殆尽
  • 通过 /proc/pid/oom_adj 调整核心应用的oom_score, 这样可以保值即使内存使用紧张, 核心应用进程也不会被oom杀死

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!