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

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

3天内不再提示

glibc内存管理存在的共性问题及解决方法

Linux阅码场 来源:Linux阅码场 作者:刘冬云 2021-06-18 14:50 次阅读

引言

对于嵌入式设备来说,用户态内存管理是一项基础功能,目前主流的用户态内存管理库有glibc、uclibc、tcmalloc、jemalloc等。

本文基于glibc2.17版本进行分析,围绕glibc内存分配原理、内存站岗问题形成原因展开讨论,并对glibc缓存大量内存(高达几十个 G甚至上百 G)且不释放的问题给出一种解决方案。

笔者遇到的问题是基于glibc进行内存管理的64 位Linux系统。具体现象如下:设备32G物理内存,在大规格打流情况下,某用户进程占用的物理内存暴涨至20G左右。

在停止打流后,观察到业务模块已经释放了绝大部分内存,但是进程占用的物理内存依然达到16G左右,此后内存状况一直维持该状态,导致系统内存紧张,若叠加上其他业务则出现了OOM的现象,已排除该进程内存泄露的可能性。

1

Glibc内存分配基本原理

Glibc使用了ptmalloc的内存管理方式,本文在描述时均使用glibc来称呼。Glibc申请内存时是从分配区申请的,分为主分配区和非主分配区,分配区都有锁,在分配内存前需要先获取锁,然后再去申请内存。

一般进程都是多线程的,当多个线程同时需要申请内存时,如果只有一个分配区,那么效率太低。

glibc为了支持多线程的内存申请释放,会在多个线程同时需要申请内存时根据cpu核数分配一定数量的分配区,将分配区分配给线程。如果线程数量较多,则会出现多个线程争用一个分配区的的情况,这里不展开。

内存申请基本原理:当用户调用malloc申请内存时,glibc会查看是否已经缓存了内存,如果有缓存则会优先使用缓存内存,返回一块符合用户请求大小的内存块。

如果没有缓存或者缓存不足则会去向操作系统申请内存(可通过brk、mmap申请内存),然后切一块内存给用户。

内存释放基本原理:当业务模块使用完毕后调用free释放内存时,glibc会检查该内存块虚拟地址上下内存块的使用状态(fast bin除外)。若其上一块内存空闲,则与上一块内存进行合并。若下一块内存空闲,则与下一块内存进行合并。如图2所示。

若下一块内存时top chunk(top chunk一直是空闲的),则看top chunk的大小是否超过一个阈值,如果超过一个阈值则将其释放给OS。

2

Glibc内存站岗及其原因

内存站岗概念:

内存站岗指的是glibc从OS申请到内存后分配给业务模块,业务模块使用完毕后释放了内存,但是glibc没有将这些空闲内存释放给OS,也就是缓存了很多空闲内存无法归还给系统的现象。

内存站岗原因:

glibc设计时就确定其内存是用于短生命周期的,因此在设计上内存释放给OS的时机是当top chunk的大小超过一个阈值时会释放top chunk的一部分内存给OS。当top chunk不超过阈值就不会释放内存给OS。

那么问题来了,若与top chunk相邻的内存块一直在使用中,那么top chunk就永远也不会超过阈值,即便业务模块释放了大量内存,达到几十个G 或者上百个G,glibc也是无法将内存还给OS的。

对于glibc来说,其有主分配和非主分配区的概念。主分配通过sbrk来增加分配区的内存大小,而非主分配区则是通过一个或多个mmap出来的内存块用链表链接起来模拟主分配区的。为了更清晰的解释内存站岗,下面举个例子来说明主分配区的内存站岗。

如上有(a) (c) (e) (g)内存块正在使用,故而导致了空闲内存(b) (d) (f)无法和top chunk连成一块更大的空闲内存块,glibc的阈值(64位系统默认是128K),尽管目前空闲内存有将近130M,也无法还给OS。

接下来看非主分配区的内存站岗,实际的非主分配区可能有很多个heap,这里假设只有4个heap。

在定位过程中,笔者与同事讨论过多次如何解决站岗。在一次讨论过程中由邓竑杰提出降低heap的size(类似于tcmalloc的做法),虽然实测后发现完全没有效果,但是为后续解决问题起到了启示作用。

后面笔者在走读代码时发现这是glibc原生机制,同时笔者在查看内存布局时观察到非主分配区大量heap均为free状态。原有机制是先释放heap3,如果heap3有内存在使用,尽管heap0、heap1、heap2的内存都释放了,那也是无法释放给系统。

glibc有多个分配区,每个分配区都几百 M 空闲内存的话,则整个进程占用达到几十个G也就不奇怪了。

3

Glibc内存站岗解决方法及patch

在内存释放时,对于主分配区和非主分配其走的流程是不一样的,我们64位系统的进程内存模型为经典模式,栈是从高地址向低地址生长的。

对于主分配区的内存站岗我还没有遇到过,若主分配区内存站岗,一种方法是可以尝试madvise将主分配区的pagesize对齐的空闲内存进行释放,但是这样效果可能不太明显。

另外一种是通过创建线程,然后将主线程的业务移到新线程即可,这样主分配区就不会造成站岗了,而将站岗转移到了非主配区,而非主分配区则是我们接下来要进行优化的主战场。

针对非主分配区进行两处优化:a) heap0,heap1,heap2是空闲的,那么我们就可以将heap1,heap2释放掉;b) heap默认是64M,降低每个heap的size(笔者测试时设置为512K)。

这里需要特别解释一下为什么不释放heap0和最后一个heap3,heap0的组成如图7所示。图左边是第一个heap即heap0,图右边是最后一个heap即heap3。

从图中可以清晰的看到如若释放掉heap0那么会将struct malloc_state结构体释放,会造成进程崩溃。右边这个由于有在用的内存,也不能释放掉。当然如果heap3的内存全部被释放了,则由glibc原生代码进行了处理,patch不再处理。

经过修改glibc源码,优化其释放机制,实际打流测试。

在打流到峰值后,进程使用了20G的内存,在停止打流后数秒内便恢复到了打流前的内存水平,进程所占用的内存基本还给系统了。至此,glibc内存站岗问题得到解决。

以上我们介绍了如何解决内存站岗的原理,纸上得来终觉浅,现在我们看patch源码实现。

目前笔者已经将该优化的patch提交到开源社区审核,提交到社区的patch未对heap的size进行修改,这是因为想要谨慎一些,毕竟开源的代码使用场景较多,如有需要可自行决定heap的size。

Patch基于glibc2.17代码

1. Index: arena.c2. ===================================================================3. --- arena.c (revision 2)4. +++ arena.c (working copy)5. @@ -652,7 +652,7 @@6.7. static int8. internal_function9. -heap_trim(heap_info *heap, size_t pad)10. +heap_trim(heap_info *heap, heap_info* free_heap, size_t pad)11. {12. mstate ar_ptr = heap-》ar_ptr;13. unsigned long pagesz = GLRO(dl_pagesize);14. @@ -659,7 +659,29 @@15. mchunkptr top_chunk = top(ar_ptr), p, bck, fwd;16. heap_info *prev_heap;17. long new_size, top_size, extra, prev_size, misalign;18. + heap_info *last_heap;19.20. + /*Release heap if possible*/21. + last_heap = heap_for_ptr(top_chunk);22. + if ((NULL != free_heap-》prev) && (last_heap != free_heap)){23. + p = chunk_at_offset(free_heap, sizeof(*free_heap));24. + if (!inuse(p)){25. + if (chunksize(p)+sizeof(*free_heap)+MINSIZE==free_heap-》size){26. + while (last_heap){27. + if (last_heap-》prev == free_heap){28. + last_heap-》prev == free_heap-》prev;29. + break;30. + }31. + last_heap = last_heap-》prev;32. + }33. + ar_ptr-》system_mem -= free_heap-》size;34. + arena_mem -= free_heap-》size;35. + unlink(p, bck, fwd);36. + delete_heap(free_heap);37. + return 1;38. + }39. + }40. + }41. /* Can this heap go away completely? */42. while(top_chunk == chunk_at_offset(heap, sizeof(*heap))) {43. prev_heap = heap-》prev;44. Index: malloc.c45. ===================================================================46. --- malloc.c (revision 2)47. +++ malloc.c (working copy)48. @@ -915,7 +915,7 @@49. # if __WORDSIZE == 3250. # define DEFAULT_MMAP_THRESHOLD_MAX (512 * 1024)51. # else52. -# define DEFAULT_MMAP_THRESHOLD_MAX (4 * 1024 * 1024 * sizeof(long))53. +# define DEFAULT_MMAP_THRESHOLD_MAX (256 * 1024)54. # endif55. #endif56.57. @@ -3984,7 +3984,7 @@58. heap_info *heap = heap_for_ptr(top(av));59.60. assert(heap-》ar_ptr == av);61. - heap_trim(heap, mp_.top_pad);62. + heap_trim(heap, heap_for_ptr(p), mp_.top_pad);63. }64. }

4

结束语

不同的内存管理方式均有其优势和缺陷,由于工作需要,笔者有幸研究过glibc、tcmalloc、uclibc内存管理,本文讨论了glibc内存管理存在的一个共性问题,并给出可行的解决方案。

对于内存站岗问题,一般的做法是用户自己缓存一些长时间不释放的内存。另一种是干脆将glibc替换为tcmalloc。因为 tcmalloc 的 span比较小,所以站岗发生的概率极低,即便发生也就站岗一个span的大小。若由于某些原因不能用tcmalloc代替glibc的场景,如上的解决思路可以尝试一下,该问题也困扰我们多时了,花费了较长时间和较多精力去定位。

在glibc2.28的版本中,glibc有了tcache的特性,对于业务进程使用大量小内存的场景则更加容易出现内存站岗问题。在撰写本文时查看了glibc2.33版本,开源社区还未对该问题进行修改(或许是开源社区大神认为这不是glibc的问题,而是用户不释放内存)。

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11222

    浏览量

    208888
  • 代码
    +关注

    关注

    30

    文章

    4742

    浏览量

    68330
  • Glibc
    +关注

    关注

    0

    文章

    9

    浏览量

    7493

原文标题:Linux glibc 内存站岗问题及解决方法

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    RAM内存不足的表现及解决方法

    RAM内存不足的表现及解决方法 一、RAM内存不足的表现 系统运行缓慢 当RAM内存不足时,计算机的响应速度会明显下降,打开程序或文件需要更长的时间。 频繁的页面交换(Page Swa
    的头像 发表于 11-11 09:53 290次阅读

    Mobaxterm 常见问题与解决方法

    强大,但用户在使用过程中可能会遇到一些问题。以下是一些常见问题及其解决方法: 1. 连接问题 问题: 无法连接到远程服务器。 解决方法: 确认服务器地址和端口号是否正确。 检查网络连接是否正常。 确认服务器是否允许SSH/Telnet等连接。 查看是否有防火墙或安全软件阻
    的头像 发表于 11-10 15:35 861次阅读

    高速PCB信号和电源完整性问题的建模方法研究

    高速PCB信号和电源完整性问题的建模方法研究
    发表于 09-21 14:13 0次下载

    CAN盒指示灯不亮的解决方法

    CAN盒指示灯不亮可能有多种原因,以下是一些常见的解决方法
    的头像 发表于 09-20 14:53 460次阅读

    接地网阻值偏大的原因及解决方法

     地网接地电阻测试仪是保障电气设备安全运行的重要组成部分,但在实际使用中,我们可能会遇到接地网阻值偏大的情况。接下来,鸿蒙小小编介绍造成这种情况的原因,并提供一些解决方法。   1、接地体积不足
    发表于 06-17 09:19

    鸿蒙OpenHarmony:【常见编译问题和解决方法

    常见编译问题和解决方法
    的头像 发表于 05-11 16:09 1965次阅读

    UPS蓄电池运维管理解析,内阻抗监测数据

    石油化工行业大量使用UPS电源,对UPS蓄电池的运维管理仍然存在不少问题。本文对电池普遍存在的使用共性问题进行归类总结,结合主动电池安全运维管理
    的头像 发表于 05-11 09:07 629次阅读
    UPS蓄电池运维<b class='flag-5'>管理</b>解析,内阻抗监测数据

    华为专利公布:内存管理方法及相关设备

    该专利主要讲述如何通过特定方法优化内存管理效率,包括确定N个具有相同虚拟地址但权限各异的进程(N必须为大于或等于2的整数),并据此建立特定映射关系表以及权限表,每一进程均对应一个权限表。
    的头像 发表于 04-16 09:51 377次阅读
    华为专利公布:<b class='flag-5'>内存</b><b class='flag-5'>管理方法</b>及相关设备

    分析电源电感发热解决方法

    电子发烧友网站提供《分析电源电感发热解决方法.docx》资料免费下载
    发表于 03-29 14:39 2次下载

    EMI电磁干扰:原理、影响及解决方法详解?

    EMI电磁干扰:原理、影响及解决方法详解?|深圳比创达电子
    的头像 发表于 03-21 10:02 834次阅读
    EMI电磁干扰:原理、影响及<b class='flag-5'>解决方法</b>详解?

    glibc malloc内存分配器的实现原理

    内存(Heap Memory)是一个很有意思的领域。你可能和我一样,也困惑于下述问题很久了。
    的头像 发表于 01-17 10:03 777次阅读
    <b class='flag-5'>glibc</b> malloc<b class='flag-5'>内存</b>分配器的实现原理

    PCB压合问题解决方法

    PCB压合问题解决方法
    的头像 发表于 01-05 10:32 1004次阅读

    网卡硬件故障及解决方法

    网卡硬件故障及解决方法 网卡是计算机与网络之间进行通信的重要组件之一。然而,由于各种原因,网卡硬件故障可能会导致计算机无法连接到网络或者网络连接缓慢不稳定。本文将介绍常见的网卡硬件故障及解决方法,并
    的头像 发表于 12-27 15:17 2808次阅读

    SNMP配置及网络管理系统无法接收告警(Trap)的解决方法

    本文将详细介绍如何配置SNMP,并探讨网络管理系统(NMS)无法接收告警(Trap)的原因及解决方法。SNMP(简单网络管理协议)是一种用于管理和监控网络设备的协议,而NMS则是用于集
    的头像 发表于 12-27 13:58 1722次阅读

    jvm管理内存包括哪几个运行时数据内存

    JVM(Java虚拟机)是Java程序的运行环境,它提供了内存管理机制来管理Java程序所需的运行时数据内存。这些运行时数据内存包括堆
    的头像 发表于 12-05 14:09 525次阅读