0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

linux内存性能优化介绍

科技绿洲 来源:Linux开发架构之路 作者:Linux开发架构之路 2023-11-10 15:23 次阅读

【1】内存映射

Linux 内核给每个进程都提供了一个独立且连续的虚拟地址空间,以便进程可以方便地访问虚拟内存;虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同字长的处理器,地址空间的范围也不同;图示为 32 位和 64 位系统的虚拟地址空间;

图片

内存映射是将虚拟内存地址映射到物理内存地址,内核为每个进程都维护了一张页表,记录虚拟地址与物理地址的映射关系;

图片

页表存储在 CPU 的内存管理单元 MMU 中,正常情况下,处理器就可以直接通过硬件,找出要访问的内存;当进程访问的虚拟地址在页表中不存在时,系统会产生一个缺页异常,进入内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行;

TLB (Translation Lookaside Buffer,转译后备缓冲器) 是 MMU 中页表的高速缓存,由于进程的虚拟地址空间是独立的,而 TLB 的访问速度又比 MMU 快得多,因此通过减少进程的上下文切换,减少 TLB 的刷新次数,可以提高 TLB 缓存的使用率,进而提高 CPU 的内存访问性能;

MMU 规定了内存映射的最小单位,即页,通常是 4 KB 大小,每一次内存映射,都需要关联 4KB 或者 4KB 整数倍的内存空间;

多级页表就是把内存分成区块来管理,将原来的映射关系改成区块索引和区块内的偏移;由于虚拟内存空间通常只用了很少一部分,多级页表就只保存这些使用中的区块,从而大大地减少页表的项数,Linux 四级页表管理内存页图示

图片

大页,即比普通页更大的内存块,常见的大小有 2MB 和 1GB;

【2】虚拟内存空间分布

32 位系统中用户空间的分段示意图

图片

    1. 只读段,包括代码和常量等
    1. 数据段,包括全局变量等
    1. 堆,包括动态分配的内存,从低地址开始向上增长
    1. 文件映射段,包括动态库、共享内存等,从高地址开始向下增长
    1. 栈,包括局部变量和函数调用的上下文等,栈的大小是固定的,一般是 8 MB

【3】内存的分配与回收

内存分配

小块内存 (小于 128K),使用 brk() 来分配,即通过移动堆顶的位置来分配内存,这些内存释放后并不会立刻归还系统,而是被缓存起来,以便重复使用;

  • brk() 方式分配内存,可以减少缺页异常的发生,提高内存访问效率;由于这些内存没有归还系统,在内存工作繁忙时,频繁的内存分配和释放会造成内存碎片;

大块内存 (大于 128K),使用内存映射 mmap() 来分配,即在文件映射段找一块空闲内存分配出去

  • mmap() 方式分配内存,会在释放时直接归还系统,因此 mmap 会发生缺页异常;在内存工作繁忙时,频繁的内存分配会导致大量的缺页异常,使内核的管理负担增大;

内存回收

回收缓存,如使用 LRU (Least Recently Used) 算法,回收最近使用最少的内存页面;

回收不常访问的内存,把不常用的内存通过交换分区直接写到磁盘中;

  • 交换分区 (Swap) 即把一块磁盘空间当成内存来用;把进程暂时不用的数据存储到磁盘中(换出),当进程访问这些内存时,再从磁盘读取这些数据到内存中 (换入)

杀死进程,内存紧张时系统还会通过 OOM (Out of Memory),直接杀掉占用大量内存的进程;

内核的一种保护机制,监控进程的内存使用情况,并且使用 oom_score 为每个进程的内存使用情况进行评分;

  • 一个进程消耗的内存越大,oom_score 就越大;
  • 一个进程运行占用的 CPU 越多,oom_score 就越小;
  • 管理员可以通过 /proc 文件系统,手动设置进程的 oom_adj,从而调整进程的 oom_score;

oom_adj 的范围是 [-17, 15],数值越大,表示进程越容易被 OOM 杀死;数值越小,表示进程越不容易被 OOM 杀死,其中 -17 表示禁止 OOM;

【4】buffer/cache

Buffer 是内核缓冲区用到的内存,对应的是 /proc/meminfo 中的 Buffers 值;

Cache 是内核页缓存和 Slab 用到的内存,对应的是 /proc/meminfo 中的 Cached 与 SReclaimable 之和;

  • Buffers 是对原始磁盘块的临时存储,即用来缓存磁盘的数据,通常不会特别大 (20MB 左右);从而内核可以把分散的写集中起来,统一优化磁盘的写入,比如可以把多次小的写合并成单次大的写等等;

Buffer 既可以用作 “将要写入磁盘数据的缓存”,也可以用作 “从磁盘读取数据的缓存”

  • Cached 是从磁盘读取文件的页缓存,即用来缓存从文件读取的数据;从而,下次访问这些文件数据时,就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘;

实际上,Cache 也会缓存写文件时的数据

Cache 既可以用作 “从文件读取数据的页缓存”,也可以用作 “写文件的页缓存”

  • SReclaimable 是 Slab 的一部分,Slab 包括两部分,其中的可回收部分,用 SReclaimable 记录;而不可回收部分,用 SUnreclaim 记录;

【5】内存泄漏

栈内存由系统自动分配和管理,一旦程序运行超出了这个局部变量的作用域,栈内存就会被系统自动回收,不会产生内存泄漏的问题;

堆内存由应用程序分配和管理,除非程序退出,这些堆内存并不会被系统自动释放,而是需要应用程序明确调用库函数 free() 释放,如果应用程序没有正确释放堆内存,就会造成内存泄漏;

只读段,包括程序的代码和常量,由于是只读的,不会再去分配新的内存,不会产生内存泄漏;

数据段,包括全局变量和静态变量,这些变量在定义时就已经确定了大小,不会产生内存泄漏;

内存映射段,包括动态链接库和共享内存,其中共享内存由程序动态分配和管理,若程序在分配后忘了回收,就会导致泄漏问题;

【6】Swap 知识点

Swap 即把一块磁盘空间或者一个本地文件当成内存来使用,包括换出和换入两个过程;

  • 换出,即把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存;
  • 换入,即在进程再次访问这些内存的时候,把它们从磁盘读到内存中来;

NUMA 与 Swap

NUMA (Non-Uniform Memory Access) 架构,在 NUMA 架构下,多个处理器被划分到不同 Node 上,且每个 Node 都拥有自己的本地内存空间,而同一个 Node 内部的内存空间,又可以进一步分为不同的内存域(Zone);

某个 Node 内存不足时,系统可以从其他 Node 寻找空闲内存,也可以从本地内存中回收内存;可以通过
/proc/sys/vm/zone_reclaim_mode 来选择模式,支持以下几个选项;

  • 默认的 0,表示既可以从其他 Node 寻找空闲内存,也可以从本地回收内存;
  • 1、2、4 都表示只回收本地内存,2 表示可以回写脏数据回收内存,4 表示可以用 Swap 方式回收内存;

swappiness

  • 对文件页的回收,即直接回收缓存,或者把脏页写回磁盘后再回收;
  • 对匿名页的回收,即通过 Swap 机制,把它们写入磁盘后再释放内存;

Linux 提供了 /proc/sys/vm/swappiness 选项,用来调整使用 Swap 的积极程度,swappiness 的范围是 0-100,数值越大,越积极使用 Swap,即更倾向于回收匿名页;数值越小,越消极使用 Swap,即更倾向于回收文件页;

降低 Swap 的使用,可以提高系统的整体性能

  1. 禁止 Swap,现在服务器的内存足够大,所以除非有必要,禁用 Swap 即可,随着云计算的普及,大部分云平台中的虚拟机都默认禁止 Swap;
  2. 若实在需要用到 Swap,可以尝试降低 swappiness 的值,减少内存回收时 Swap 的使用倾向;
  3. 响应延迟敏感的应用,如果它们可能在开启 Swap 的服务器中运行,你还可以用库函数 mlock() 或者 mlockall() 锁定内存,阻止它们的内存换出;

【7】Linux 回收内存的时机

在内存资源紧张时,Linux 通过直接内存回收和定期扫描的方式,释放文件页和匿名页,以便把内存分配给更需要的进程使用;

直接内存回收,存在新的大块内存分配请求,但是剩余内存不足,此时系统就需要回收一部分内存,进而尽可能地满足新内存请求;

kswapd0 内核线程,用于定期回收内存,kswapd0 定义了三个内存阈值,分别是页最小阈值 (pages_min)、页低阈值(pages_low) 和页高阈值(pages_high);pages_free 表示剩余内存;

图片

  • 剩余内存小于页最小阈值,说明进程可用内存都耗尽了,只有内核才可以分配内存;
  • 剩余内存落在页最小阈值和页低阈值中间,说明内存压力比较大,剩余内存不多了;这时 kswapd0 会执行内存回收,直到剩余内存大于高阈值为止;
  • 剩余内存落在页低阈值和页高阈值中间,说明内存有一定压力,但还可以满足新内存请求;
  • 剩余内存大于页高阈值,说明剩余内存比较多,没有内存压力;

页低阈值可以通过内核选项
/proc/sys/vm/min_free_kbytes 来间接设置,min_free_kbytes 设置了页最小阈值,而其他两个阈值,都是根据页最小阈值计算生成,如下

pages_low  = pages_min * 5 / 4
pages_high = pages_min * 3 / 2

性能指标与工具总结

图片

图片

实战记录

【1】free 命令

图片

  • total 总内存大小;
  • used 已使用内存的大小,包含了共享内存;
  • free 未使用内存的大小;
  • shared 共享内存的大小;
  • buff/cache 缓存和缓冲区的大小;
  • available 新进程可用内存的大小;

【2】top 命令

图片

VIRT 是进程虚拟内存的大小,只要是进程申请过的内存,即便还没有真正分配物理内存,也会计算在内;

RES 是常驻内存的大小,即进程实际使用的物理内存大小,但不包括 Swap 和共享内存;

SHR 是共享内存的大小,比如与其他进程共同使用的共享内存、加载的动态链接库以及程序的代码段等;

%MEM 是进程使用物理内存占系统总内存的百分比;

注意

    1. 虚拟内存通常并不会全部分配物理内存;
    1. 共享内存 SHR 并不一定是共享的,如程序的代码段、非共享的动态链接库,也都算在 SHR 里;

【3】磁盘和文件写案例

【3.1】案例一,写文件

测试命令
$ dd if=/dev/urandom of=/tmp/file bs=1M count=500

监控命令
vmstat 2

图片

    1. 在 Cache 刚开始增长时,块设备 I/O 很少,而过一段时间后,才会出现大量的块设备写;
    1. 当 dd 命令结束后,Cache 不再增长,但块设备写还会持续一段时间,并且多次 I/O 写的结果加起来是 dd 要写的 500M 的数据;

【3.2】案例二,写磁盘

测试命令
# 运行dd命令向磁盘分区/dev/sdb1写入2G数据
$ dd if=/dev/urandom of=/dev/sdb1 bs=1M count=2048

监控命令
vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
1  0      0 7584780 153592  97436    0    0   684     0   31  423  1 48 50  2  0
 1  0      0 7418580 315384 101668    0    0     0     0   32  144  0 50 50  0  0
 1  0      0 7253664 475844 106208    0    0     0     0   20  137  0 50 50  0  0
 1  0      0 7093352 631800 110520    0    0     0     0   23  223  0 50 50  0  0
 1  1      0 6930056 790520 114980    0    0     0 12804   23  168  0 50 42  9  0
 1  0      0 6757204 949240 119396    0    0     0 183804   24  191  0 53 26 21  0
 1  1      0 6591516 1107960 123840    0    0     0 77316   22  232  0 52 16 33  0
    1. 写磁盘时 (即 bo 大于 0 时),Buffer 和 Cache 都在增长,但显然 Buffer 的增长快得多;

【4】磁盘和文件读案例

【4.1】案例一、读文件

测试命令
# 运行dd命令读取文件数据
$ dd if=/tmp/file of=/dev/null

监控命令
vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  1      0 7724164   2380 110844    0    0 16576     0   62  360  2  2 76 21  0
 0  1      0 7691544   2380 143472    0    0 32640     0   46  439  1  3 50 46  0
 0  1      0 7658736   2380 176204    0    0 32640     0   54  407  1  4 50 46  0
 0  1      0 7626052   2380 208908    0    0 32640    40   44  422  2  2 50 46  0
  • 读取文件时 (即 bi 大于 0 时),Buffer 保持不变,而 Cache 则在不停增长;

【4.2】案例二、读磁盘

测试命令
# 运行dd命令读取文件
$ dd if=/dev/sda1 of=/dev/null bs=1M count=1024

监控命令
vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
0  0      0 7225880   2716 608184    0    0     0     0   48  159  0  0 100  0  0
 0  1      0 7199420  28644 608228    0    0 25928     0   60  252  0  1 65 35  0
 0  1      0 7167092  60900 608312    0    0 32256     0   54  269  0  1 50 49  0
 0  1      0 7134416  93572 608376    0    0 32672     0   53  253  0  0 51 49  0
 0  1      0 7101484 126320 608480    0    0 32748     0   80  414  0  1 50 49  0
  • 读磁盘时 (也就是 bi 大于 0 时),Buffer 和 Cache 都在增长,但显然 Buffer 的增长快很多;

附录

【1】文件系统与磁盘的区别

磁盘是一个存储设备(块设备),可以被划分为不同的磁盘分区,而在磁盘或者磁盘分区上,还可以再创建文件系统,并挂载到系统的某个目录中,这样,系统就可以通过这个挂载目录,来读写文件;即磁盘是存储数据的块设备,也是文件系统的载体;文件系统需要通过磁盘,来保证数据的持久化存储;

在读写普通文件时,I/O 请求会首先经过文件系统,然后由文件系统负责,来与磁盘进行交互;在读写块设备文件时,会跳过文件系统,直接与磁盘交互,也就是所谓的 “裸 I/O”;

【2】统计所有进程的物理内存使用量

每个进程的 PSS ,是指把共享内存平分到各个进程后,再加上进程本身的非共享内存大小的和;

# 使用grep查找Pss指标后,再用awk计算累加值
$ grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {printf "%d kBn", total }'
391266 kB
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 处理器
    +关注

    关注

    68

    文章

    18288

    浏览量

    222176
  • Linux
    +关注

    关注

    87

    文章

    10990

    浏览量

    206738
  • 内存
    +关注

    关注

    8

    文章

    2767

    浏览量

    72772
  • 缓存
    +关注

    关注

    1

    文章

    220

    浏览量

    26444
收藏 人收藏

    评论

    相关推荐

    linux内存相关知识科普

    linux 内存组织结构和页面布局,内存碎片产生原因和优化算法。
    发表于 08-08 10:57 312次阅读

    走进Linux内存系统探寻内存管理的机制和奥秘

    Linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍
    的头像 发表于 01-05 09:47 1379次阅读

    Linux内存相关知识科普

    Linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍**Linu****x
    发表于 07-25 14:43 554次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内存</b>相关知识科普

    查看Linux系统内存使用情况的几种方法

    Linux系统中,内存监控是优化系统性能的关键。本文为你介绍12种方法,帮助你全面掌握Linux
    的头像 发表于 11-13 09:30 1747次阅读
    查看<b class='flag-5'>Linux</b>系统<b class='flag-5'>内存</b>使用情况的几种方法

    Linux系统的性能优化策略

    近年来,世界上许多大软件公司纷纷推出各种Linux服务器系统及Linux下的应用软件。目前,Linux 已可以与各种传统的商业操作系统分庭抗礼,在服务器市场,占据了相当大的份额。本文分别从磁盘调优,文件系统,
    发表于 07-16 06:23

    合理的使用linux系统的内存

    关注、星标公众号,不错过精彩内容来源:EDN电子技术设计linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要
    发表于 12-17 07:40

    基于Linux的Socket网络编程的性能优化

    基于Linux的Socket网络编程的性能优化 随着Intenet的日益发展和普及,网络在嵌入式系统中应用非常广泛,越来越多的嵌入式设备采用Linux操作系统。
    发表于 10-22 20:48 995次阅读
    基于<b class='flag-5'>Linux</b>的Socket网络编程的<b class='flag-5'>性能</b><b class='flag-5'>优化</b>

    Android性能优化之Java内存

    在Android开发中,一些不好的编程习惯会导致我们的开发的app存在内存泄露的情况。下面介绍一些在Android开发中常见的内存泄露优化方案。
    发表于 04-26 15:38 880次阅读
    Android<b class='flag-5'>性能</b><b class='flag-5'>优化</b>之Java<b class='flag-5'>内存</b>

    Linux性能及调优指南:内存架构

    超过4GB。从性能的角度来看,理解32位和64位系统中Linux内核如何把物理内存映射到虚拟内核是重要的。从图1-10中,可以看出Linux内核在处理32位和64位系统
    发表于 04-02 14:32 265次阅读

    Linux CPU的性能应该如何优化

    Linux系统中,由于成本的限制,往往会存在资源上的不足,例如 CPU、内存、网络、IO 性能。本文,就对 Linux 进程和 CPU 的原理进行分析,总结出 CPU
    的头像 发表于 01-18 08:52 3138次阅读

    一文解析Linux内存系统

    Linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍
    的头像 发表于 09-01 10:46 2232次阅读
    一文解析<b class='flag-5'>Linux</b><b class='flag-5'>内存</b>系统

    嵌入式linux+io+优化,嵌入式Linux系统内存优化使用方法研究

    优化进而确保响应运行。并且经过实践证明,嵌入式系统内存优化使用,能够提升系统空间5%内存,确保系统顺利运行。【关键词】 嵌入式 Linux
    发表于 11-01 16:31 10次下载
    嵌入式<b class='flag-5'>linux</b>+io+<b class='flag-5'>优化</b>,嵌入式<b class='flag-5'>Linux</b>系统<b class='flag-5'>内存</b><b class='flag-5'>优化</b>使用方法研究

    Linux系统的共享内存的使用

    但有时候为了让不同进程之间进行通信,需要让不同进程共享相同的物理内存Linux通过 共享内存 来实现这个功能。下面先来介绍一下Linux
    的头像 发表于 11-14 11:55 971次阅读

    Linux驱动模块.ko内存精简优化过程

    Linux 驱动模块可以独立的编译成 .ko 文件,虽然大小一般只有几 MB,但对总内存只有几十 MB 的小型 Linux 系统来说,常常也是一个非常值得优化的点。本文以一个实际例子,
    发表于 09-25 09:23 528次阅读

    Linux内核slab性能优化的核心思想

    今天分享一篇内存性能优化的文章,文章用了大量精美的图深入浅出地分析了Linux内核slab性能优化
    的头像 发表于 11-13 11:45 330次阅读
    <b class='flag-5'>Linux</b>内核slab<b class='flag-5'>性能</b><b class='flag-5'>优化</b>的核心思想