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
中查看他们的大小第三种 oom机制安装 oom_score给进程排序, oom_score越大 进程就越容易被系统杀死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日志 /var/log/dmesg
1 |
|
衡量缓存使用的好坏指标?
缓存命中率
- cachestat : 提供了整个操作系统缓存的读写命中情况
- cachetop : 提供了每个进程的缓存命中情况
内存泄漏如何处理
内存的分配和回收
一般在局部定义一个变量,比如 int data[64] 就定义了一个可以存储64字节的内存段, 由于是一个局部变量 所以他会在
内存空间的栈
中分配内存.
栈内存 由系统自动分配和管理, 所以程序一旦超出 就会被系统自动回收, 就不会产生 内存泄漏的问题.
但是很多时候我们不知道数据大小, 所以要用到标准函数库 malloc()_, 在程序中动态分配内存, 这时候 系统会从内存空间的堆
中分配内存.
堆内存由应用程序自己分配和管理,除非程序自己退出, 不然这些堆内存不会被自动释放,所以就要用到应用程序明确调用库函数 free()来释放, 如果应用程序没有正确释放,那么就会造成内存泄漏.内存泄漏危害非常大,如果忘记释放内存,不仅应用自己无法访问,系统也不能把他们的空间自此分配给其他应用, 最终内存耗尽,gg
但是系统最终可以通过 OOM(out of momeory) 机制杀死进程,但是进程在OOM之前 以及引发了一连串的反应,遭到严重影响,
内存泄漏分析工具
memleak
是bbc软件包中的一个工具, 安装目录 /usr/share/bcc/tools/memleak
分析容器中的应用常见问题: 调用栈不能正常显示,应为容器和外表隔离 所以参考 perf 方法中的案例, 如果单一二进制的应用程序 直接复制出来 运行 也可以
1 |
|
为何很多程序比如 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 协议 ,转载请注明出处!