【精品】内存性能优化-看这一篇就够啦

背景

        本文主要介绍内存性能优化相关知识,以及遇到相关问题的分析思路及优化思路。

内存问题,一般体现在两个方面。

  • 内存泄漏。

  • 性能不足。

         内存泄露,我们可以通过观察内存剩余量的变化,很容易判断是否发生了内存泄漏。

        内存性能不足,从而导致应用程序运行效率降低,或者阻塞,这一点是我们不容易分辨的。经过《CPU性能优化——“瑞士军刀“-CSDN博客》,《I/O性能优化——这一篇就足够啦-CSDN博客》章节的介绍。内存一旦出现性能瓶颈,一般也引起CPU或I/O的负载增加。比如缓存不足时,可能会导致磁盘I/O的读写增加,从而I/O 读写频率增加CPU 的iwait 时间增加。对于这种场景,我们该一步步分析呢?

概念介绍

        首先需要熟悉以下概念,只有了解linux 对内存的定义,以及内部的实现原理,我们才能从调试数据背后发现其原因,针对“下药”。

为什么引入虚拟内存

        虚拟内存相对应的则是物理内存。这两个概念也是我们老生常谈的话题。但是我们真正了解过,虚拟内存的意义和作用吗?

        回答这个问题前,我们不妨做这样的一个假设,如果没有虚拟内存,程序员操作的都是物理内存,会带来怎样的问题?【在编译原理中,曾介绍过,早期的计算机是没有虚拟内存概念的】

从我的角度而言,我觉得会产生以下两点问题:

  1. 进程运行时,会尽可能的将程序所用到的资源全部加载到内存中。然而内存资源是稀缺的。当越来越多的进程需要内存时,某些进程会因为得不到内存而无法运行。

  2. 内存容易被破坏,影响其它进程的运行。无形给程序员编码增加了难度,让编程不再这么简单。

        我们知道虚拟内存是物理内存和进程(程序员)之间的中间层。它为我们隐藏了物理内存的概念,从而提供三大能力。

  1. 高效使用内存。VM将主存看成是存储在磁盘上的地址空间的高速缓存,主存中保存热数据,根据需要在磁盘和主存之间传输数据。

  2. 简化内存管理。VM为每一个进程提供了一致的4G空间,从而简化了链接,加载,内存共享等过程。比如,程序的起始运行地址为0x080480000(0x080400000)。

  3. 内存保护。保护每个进程的地址空间不被其它进程破坏。每个进程所关联的物理内存由内核管理。若越界访问,会产生异常,不影响其它程序。

页表的工作原理:

  1. cpu想访问虚拟地址所在的虚拟页(VP3),根据页表,找出页表中对应的页表项,判断有效位。

  2. 若有效位为1,则DRMA缓存命中,根据物理页号,找到物理页当中的内容,返回。

  3. 若有效位为0,则发生缺页异常,调用内核缺页异常处理程序。内核通过页面置换算法选择一个页面作为被覆盖的页面,将该页的内容刷新到磁盘空间当中。然后把VP3映射的磁盘文件内容缓存到该物理地址对应的页上面(这个过程会涉及到磁盘的读写操作,因此程序运行过程中,若发生多次缺页异常,则会大大影响应用层序运行效率)。然后将页表项中的有效位变成1,第二部分存储了对应的物理内存也的地址。

  4. 缺页异常处理完毕后,返回中断前的指令,重新执行,此时缓存命中,执行1。

  5. 将找到的内容映射到高速缓存当中,CPU从高速缓存中获取该值,结束。

综上所知:

当进程创建时,内核会为进程分配4G的虚拟内存,当进程还没有开始运行时,这只是一个内存布局。实际上不会立刻把虚拟内存中的代码段,数据段拷贝到物理内存中。只是建立好虚拟内存和磁盘文件(elf文件格式)之间的映射关系(存储器映射)。这个时候.data,.text还在磁盘上。运行时发生缺页异常。于是将磁盘上的数据拷贝到物理内存中。这便是虚拟内存提供的魅力。

内存碎片

        内存碎片的概念常被人提及,但是很少会有确切的感受。我们在申请内存时,一般都是通过C标准库malloc接口实现,但它底层实现的系统调用,估计很多人没有在意过,其原理则是造成内存碎片的原因。

1. malloc 在申请小于128k的内存时,使用brk系统调用分配内存,将_edata往高地址推

2. malloc 在申请大于128K的内存时,使用mmap系统调用分配内存,在堆和栈之间找一块空闲内存分配。

这两种系统调用发生之后,并没有真正分配内存。只有在首次访问时才分配,也就是通过缺页异常进入内核,再由内核分配内存

区别:使用brk()申请的内存,这些内存在释放后不会立刻归还系统,而是被缓存起来(当系统资源紧张时,也可以回收);而mmap方式分配的内存,会在释放时立刻归还系统。请注意,这里的内存释放包括物理内存和虚拟内存。如下图:

优缺点

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

  • 而 mmap() 方式分配的内存,会在释放时直接归还系统,所以每次 mmap 都会发生缺页异常。在内存工作繁忙时,频繁的内存分配会导致大量的缺页异常。

由上可知:

内存碎片的出现是因为应用程序频繁的申请,释放大小不一的小内存导致的。内存碎片问题可能不会立即导致明显的性能下降,但长期积累可能会对系统的稳定性和性能产生不良影响。

缺页异常也会导致程序性能降低

内存的回收机制

  系统不会任由程序将内存消耗殆尽。在发生内存紧张时,系统会通过一系列机制来回收内存。

  • 回收内存。

  • 杀死进程。

第一种回收内存的方式有两类:

1. 回收缓存和缓冲区。在《I/O性能优化》章节中,讲述了虚拟文件系统为了增加数据的读写速度,会将磁盘中的数据在内存中进行缓存。也就是我们buffer/cahce参数。操作系统可以将该部分内存回收,从而提高剩余可用内存。

yihua@ubuntu:~$ free -h
              total        used        free      shared  buff/cache   available
Mem:           3.8G        1.3G        619M         31M        1.9G        2.2G
Swap:          2.0G        111M        1.9G

2. 交换分区。将不常访问的内存,保存至磁盘中。等下次访问时,再将这部分数据,从磁盘拷贝到内存中。

第二种是杀死进程的方式,就是内核的一种保护机制(OOM),它监控简称的内存使用情况,并且使用oom_score 为每个进程的内存使用情况进行评分。

  • 一个进程消耗的内存越大,oom_score 就越大;

  • 一个进程运行占用的 CPU 越多,oom_score 就越小。

这样,进程的 oom_score 越大,代表消耗的内存越多,也就越容易被 OOM 杀死,从而可以更好保护系统。

但是若应用程序本身的业务需求导致内存使用较高。如何确保在发生OOM时,不优先kill 该核心进程呢?

可以通过/proc 文件系统,手动设置进程的oom_adj,从而调整进程的oom_sore。比如将核心进程的oom_adj调整为-16,这样就不容易被OOM杀死。

echo -16 > /proc/$(pidof otamaster)/oom_adj

由上可知:

  1. 回收内存的方式,会导致程序的运行效率降低。

  2. 由于OOM机制的存在,若核心进程占用内存较多,需要调整进程的oom_adj值

性能指标

        对于内存性能分析,我们要知道从哪些指标可以分析内存性能。

系统内存指标

        系统的内存性能,我们可以关注以下指标。

  • 已用内存。系统已使用的内存,free 命令可查看。

  • 剩余内存。系统未使用的内存,free 命令可查看。

  • 可用内存。新进程可用的内存,free 命令可查看。

  • 缺页异常。缺页异常分为两个场景。可以通过ps 和top 命令查看。top 命令默认不会展示缺页异常,可以通过F->nMaj&nMin选择。

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

    • 需要磁盘 I/O 介入(比如 Swap)时,被称为主缺页异常

yihua@ubuntu:~$ ps -ef -o majflt,minflt,pid,user,comm
MAJFLT MINFLT    PID USER     COMMAND
     0   1905  59570 yihua    bash
     0    236  59737 yihua     \_ ps
     0  38832  24665 yihua    bash

  • 缓存/缓冲区。buff/cache的大小,可以被系统回收。free 命令可查看。

    • 其中buff,表示对磁盘的读写缓存。

    • cache,表示对文件的读写缓存以及slab的缓存。

  • slabs。应用程序在释放小内存时,slab 分配器不会立即释放给操作系统,而是缓存起来。提高内存利用率,以及减少内存碎片。

进程内存指标

进程的内存性能,我们可以关注以下指标。

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

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

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

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

  • Swap 内存,是指通过 Swap 换出到磁盘的内存。

由上可知:

  1. 通过free命令,可以快速查看系统的内存使用情况。

  2. 通过top 命令,可以分析进程内存的使用情况。

性能分析工具

free

yihua@ubuntu:~$ free -h
              total        used        free      shared  buff/cache   available
Mem:           3.8G        1.3G        596M         31M        1.9G        2.2G
Swap:          2.0G        111M        1.9G
  • 第一列,total 是总内存大小;

  • 第二列,used 是已使用内存的大小,包含了共享内存;

  • 第三列,free 是未使用内存的大小,;

  • 第四列,shared 是共享内存的大小,包括动态库;

  • 第五列,buff/cache 是缓存和缓冲区的大小;

  • 最后一列,available 是新进程可用内存的大小;不仅包含了未使用内存,也包含了可回收内存buff/cache;

free可以知道当前系统内存的静态状况。

vmstat

yihua@ubuntu:~$ vmstat 1 5
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 114176 611012 325036 1698592    0    0     4    11    1   10  0  0 100  0  0
 0  0 114176 611012 325036 1698592    0    0     0     0   48   82  0  0 100  0  0
 0  0 114176 611128 325036 1698592    0    0     0     0   53   91  0  0 100  0  0
 0  0 114176 611128 325036 1698592    0    0     0     0   47   78  0  0 100  0  0
 0  0 114176 611128 325036 1698592    0    0     0     0   55   86  0  0 100  0  0

关注以下参数:

  1. swap的换入/换出。si/so

  2. 内存部分的buffcache

  3. I/O的读写。bi/bo

vmstat可以知道当前系统内存变化情况。

cachestat

$ cachestat 1 3
   TOTAL   MISSES     HITS  DIRTIES   BUFFERS_MB  CACHED_MB
       2        0        2        1           17        279
       2        0        2        1           17        279
       2        0        2        1           17        279 
  • TOTAL ,表示总的 I/O 次数;

  • MISSES ,表示缓存未命中的次数;

  • HITS ,表示缓存命中的次数

  • DIRTIES, 表示新增到缓存中的脏页数,即slab未释放给系统的缓存

  • BUFFERS_MB 表示 Buffers 的大小,以 MB 为单位;

  • CACHED_MB 表示 Cache 的大小,以 MB 为单位。

cachestat 可以查看当前系统的缓存命中率。

cachetop

$ cachetop
11:58:50 Buffers MB: 258 / Cached MB: 347 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
   13029 root     python                  1        0        0     100.0%       0.0%
  • MISSES ,表示缓存未命中的次数;

  • HITS ,表示缓存命中的次数;

  • DIRTIES, 表示新增到缓存中的脏页数,即slab未释放给系统的缓存;

  • READ_HIT%,读缓存命中率。

  • WRITE_HIT%,写缓存命中率。

cachetop 可以查看进程的缓存命中率状况。

pcstat

$ ls
$ pcstat /bin/ls
+---------+----------------+------------+-----------+---------+
| Name    | Size (bytes)   | Pages      | Cached    | Percent |
|---------+----------------+------------+-----------+---------|
| /bin/ls | 133792         | 33         | 33        | 100.000 |
+---------+----------------+------------+-----------+---------+

pcstat查看文件在内存中的缓存大小以及缓存比例

内存优化套路

        关于内存的问题,我们常遇到的一般就是内存泄漏。而缺页异常,swap机制,缓存不足等导致的性能问题,一般很少遇到。

  1. 内存泄漏问题。

    1. 通过free命令查看系统剩余内存是否充足。若持续减少,且不趋于稳定,则说明出现内存泄漏。

    2. 通过toppidstat命令查看哪一个进程内存持续增加。

    3. 通过memleakvalgrind查看进程中内存分配点。

    4. 定位到代码,进行分析。

  2. 性能问题。内存性能问题一般的体现就是运行效率降低,对于实时性要求不高的程序也很难感知。

    1. 通过freetop命令,查看系统的剩余内存,缓存/缓冲区,swap资源等。

    2. 通过vmstat命令,查看系统内存资源的变化趋势,确认内存问题类型。

      1. 若是swap导致的性能问题。可以尝试关闭swap机制。

      2. 若是缓存/缓存区过多。可以尝试设置/proc/sys/vm/min_free_kbytes调整定期回收内存的繁殖。

      3. 若是大量发生缺页异常。可以在编码过程中建立缓存。

其中内存泄漏的排查套路,可参考《【精选】快速定位内存泄漏的套路_内存泄露log找哪些关键字-CSDN博客》。

性能问题案例,后续待补充。

常见优化方向

内存泄漏问题属于代码bug,需要我们修改代码。但是内存相关的优化,可以提高我们程序的运行效率。在我看来,内存调优最重要的就是:保证应用程序的热点数据放到内存中,并尽量减少换页和交换

  • 最好禁止 Swap。如果必须开启 Swap,降低 swappiness 的值,减少内存回收时 Swap 的使用倾向。

  • 减少内存的动态分配。比如,可以使用内存池、大页(HugePage)等。

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

  • 使用 cgroups 等方式限制进程的内存使用情况。这样,可以确保系统内存不会被异常进程耗尽。

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

总结

        工作中我们常遇到的内存问题就是内存泄漏。若是内存导致的效率问题,一般情况会比较少。即使遇到了,希望本文也能给予你分析和解决的思路。

 若我的内容对您有所帮助,还请关注我的公众号。不定期分享干活,剖析案例,也可以一起讨论分享。
我的宗旨:
踩完您工作中的所有坑并分享给您,让你的工作无bug,人生尽是坦途

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 华为HCIP精品课程笔记-Wakin是一本非常有价值的学习资料。该笔记通过详细介绍HCIP认证的相关知识点和实用技能,为学生提供了一种高效学习的方式。 首先,华为HCIP精品课程笔记-Wakin涵盖了HCIP认证考试需要掌握的全部知识点。对于想要通过HCIP考试的学生来说,这本笔记提供了一个简明扼要的指南,可以帮助他们系统地学习和复习相关知识。笔记中的内容分为多个章节,涵盖了网络技术、路由器和交换机、路由控制、IP多播、IPv6和MPLS等重要主题。每个主题都有详细的解释和示例,帮助学生更好地理解和掌握。 其次,华为HCIP精品课程笔记-Wakin还包含了一些实用技能和案例分析。这些内容能帮助学生更好地理解和应用所学知识。对于在实际工作中需要应用HCIP技能的人来说,这本笔记提供了一些宝贵的经验和建议。 此外,华为HCIP精品课程笔记-Wakin还提供了一些习题和练习题,可以帮助学生检验自己的学习成果。通过对这些习题的练习,学生可以更好地了解自己的薄弱点,并进行有针对性的复习和提高。 总之,华为HCIP精品课程笔记-Wakin是一本非常实用和有价值的学习资料。它提供了一种高效的学习方式,帮助学生系统地掌握和应用HCIP认证的相关知识和技能。我强烈推荐这本笔记给所有想要通过HCIP考试或者在实际工作中应用HCIP技能的人。 ### 回答2: 《华为HCIP精品课程笔记-wakin》是一本非常有价值的学习资料。这本书由华为公司精心编撰而成,旨在帮助学员高效学习和掌握华为认证网络工程师(HCIP)认证所需的知识和技能。 这本笔记深入浅出地介绍了HCIP认证相关的重要概念、原理和应用。其中包括了网络架构设计、路由与交换技术、安全技术、无线网络技术等内容。每个主题都有详细的解释、示意图和实例,使读者能更好地理解和应用知识。 除了内容丰富全面外,这本笔记还具有一些独特的优点。首先,它采用了华为独有的学习方法,系统化地梳理了知识结构,使读者能更加有条理地学习。其次,每个章节都附带了重点整理的要点,方便读者快速回顾和温习。此外,为了帮助读者更好地理解,笔记还提供了一些实验和实际案例,使学习更加实践性和深入。 通过学习《华为HCIP精品课程笔记-wakin》,读者将能全面了解和掌握HCIP认证所需的知识和技能。这些知识和技能不仅适用于工作中的网络工程师,也对于其他相关岗位的人员有很大的帮助。无论是对于初学者还是对于有一定经验的人来说,这本书都是一本非常实用的学习资料。强烈推荐给所有对网络工程感兴趣的人士。 ### 回答3: 华为HCIP精品课程笔记-Wakin是一份非常有价值的学习资料。这份笔记由华为公司的专业培训师Wakin编写,对于想要学习和提升HCIP认证的人来说,是一份非常实用的参考资料。 Wakin在这份笔记中,详细地介绍了HCIP的知识点和考试重点。他从网络基础、路由交换、安全技术、无线网络等多个方面入手,深入浅出地解释了每个知识点的概念和原理。在每个章节中,Wakin都给出了一些实际案例和实验,帮助我们更好地理解和应用所学内容。 此外,Wakin在笔记中还提供了一些学习方法和技巧。他建议我们在学习过程中,要注重实践,通过自己动手实验和配置设备来加深对知识的理解。他还推荐了一些学习资源和参考书籍,帮助我们更好地补充和扩展所学知识。 总的来说,华为HCIP精品课程笔记-Wakin非常全面且易于理解。无论是准备HCIP认证考试的人,还是想要进一步提升自己网络技术的人,都可以从中受益匪浅。我相信,只要认真学习并灵活运用这份笔记中的知识,就能在网络领域中取得更好的成绩和发展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢艺华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值