首页 > 系统相关 >内存暴涨问题细探

内存暴涨问题细探

时间:2023-08-17 17:34:07浏览次数:43  
标签:malloc 调用 sbrk mmap 细探 brk 内存 暴涨

1.  进程虚拟空间

进程虚拟空间如下图:

  

如上图示:最高的1G空间保留给内核使用。接下来是栈,栈向低地址方向延伸(栈的大小受RLIMIT_STACK限制,默认为8M),下面是MMAP区(文件映射内存,如动态库等,SPP微线程的私有栈也位于这里),下面是堆(动态内存增长),堆向高地址方向延伸,接下来依次是BSS、数据段、代码段。

2. Linux下动态内存分配实现机制

C、C++的动态内存分配、管理都是基于malloc和free的,动态内存即虚拟空间堆区。另外多说一句,malloc和free操作的也是虚拟地址空间。

malloc,动态内存分配函数。是通过brk(sbrk)和mmap这两个系统调用实现的。

结合上文进程虚拟空间图,brk(sbrk)是将数据段(.data)的最高地址指针_edata往高地址推。mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存。这两种实现方式的区别大致如下:

brk(sbrk),性能损耗少; mmap相对而言,性能损耗大

mmap不存在内存碎片(是物理页对齐的,整页映射和释放); brk(sbrk)可能存在内存碎片(由于new和delete的顺序不同,可能存在空洞,又称为碎片)

无论是通过brk(sbrk)还是mmap调用分配的内存都是虚拟空间的内存,只有在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。

delete,动态内存释放函数。如果是brk(sbrk)分配的内存,直接调用brk(sbrk)并传入负数,即可缩小Heap区的大小;如果是mmap分配的内存,调用munmap归还内存。无论这两种那种处理方式,都会立即缩减进程虚拟地址空间,并归还未使用的物理内存给操作系统。

 

brk(sbrk)和mmap都是系统调用,如果程序中频繁的进行内存的扩张和收缩,每次都直接调用,当然可以实现内存精确管理的目的,但是随之而来的性能损耗也很显著。目前大多数运行库(glibc)等都对内存管理做了一层封装,避免每次直接调用系统调用影响性能。如此,就涉及到运行库的内存分配的算法问题了。

在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk,mmap,munmap这些系统调用实现的。

3.缺页中断

如何查看进程发生缺页中断的次数?
用ps -o majflt,minflt -C program命令查看。
majflt代表major fault,中文名叫大错误,minflt代表minor fault,中文名叫小错误。这两个数值表示一个进程自启动以来所发生的缺页中断的次数。


发成缺页中断后,执行了那些操作?
当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作:
1、检查要访问的虚拟地址是否合法
2、查找/分配一个物理页
3、填充物理页内容(读取磁盘,或者直接置0,或者啥也不干)
4、建立映射关系(虚拟地址到物理地址)
重新执行发生缺页中断的那条指令
如果第3步,需要读取磁盘,那么这次缺页中断就是majflt,否则就是minflt。

 

查看物理内存页使用情况:cat /proc/$PID/smaps,里面详细记录了该进程使用的物理页内存情况,如Private_Dirty、Private_Clean等
mmap系统调用:读写MMAP映射区,相当于读写被映射的文件。本意是将文件当作内存一样读写。相比Read、Write,减少了内存拷贝(Read、Write一个硬盘文件,需要先将数据从内核缓冲区拷贝到应用缓冲区(read),然后再将数据从应用缓冲区拷贝回内核缓冲区(write)。mmap直接将数据从内核缓冲区映拷贝到另一个内核缓冲区),但是被修改的数据从MMAP区同步到磁盘文件上,依赖于系统的页管理算法,默认会慢条斯理得将内容写到磁盘上。另外提供了msync强制同步到磁盘上。


4.Glibc内存分配算法

glibc的内存分配算法,是基于dlmalloc实现的ptmalloc,dlmalloc详细可以参考A Memory Allocator或者Glibc内存分配器。这里主要讲下和内存归还策略相关的,其他内容不做过多扩展。

整体来说,glibc采用的是dlmalloc。为了避免频繁调用系统调用,它内部维护了一个内存池,方便reuse,又称为free-list或bins,如下图示

 

 

所有调用delete释放的内存,并不是立即调用brk(sbrk)归还给操作系统,而是先将这个内存块挂在free-list(bins)里面,然后进行内存归并(可选操作,相邻的可用内存块合并为更大的可用内存块),并检查是否达到malloc_trim的threshhold,如果达到了,则调用malloc_trim归还部分可用内存给操作系统。
glibc中,设置了默认进行malloc_trim的threshhold为128K,也就是说当dlmalloc管理的内存池中最大可用内存>128K时,就会执行malloc_trim操作,归还部分内存给操作系统;而在可用内存<=128K时,及时程序中delete了这部分内存,这些内存也是不会归还给操作系统的。表现为:调用delete之后,进程占用的内存并没有减少。

另外,部分glibc的默认设置如下:

DEFAULT_MXFAST             64 (for 32bit), 128 (for 64bit) // free-list(fastbin)最大内存块 DEFAULT_TRIM_THRESHOLD     128 * 1024 // malloc_trim的门槛值 128k DEFAULT_TOP_PAD            0 DEFAULT_MMAP_THRESHOLD     128 * 1024 // 使用mmap分配内存的门槛值 128k DEFAULT_MMAP_MAX           65536 // mmap的最大数量

这些参数都可以通过mallopt进行调整。
malloc_trim(0)可以立即执行trim操作,将内存还给操作系统。
具体fastbin相关的内容,此处不做介绍,前期有很多基于fastbin的堆溢出攻击,感兴趣的同学可以google关键字fastbin搜索下。

 

5.考虑其他开源库的解决方案

glibc大内存是128k,可以使用tcmalloc或者jemalloc来进行内存管理

tcmalloc是Google开源的一个内存管理库

小对象(<=32K),大对象4k

jemalloc是facebook推出的

Small: [8], [16, 32, 48, …, 128], [192, 256, 320, …, 512], [768, 1024, 1280, …, 3840]

Large: [4 KiB, 8 KiB, 12 KiB, …, 4072 KiB]

Huge: [4 MiB, 8 MiB, 12 MiB, …]

6.参考资料

摘自:一次"内存泄漏"引发的血案

 内存泄漏之malloc_trim

 

 

标签:malloc,调用,sbrk,mmap,细探,brk,内存,暴涨
From: https://www.cnblogs.com/guxuanqing/p/17638291.html

相关文章

  • Java应用堆外内存泄露问题排查 | 京东云技术团队
    问题是怎么发现的最近有个java应用在做压力测试压测环境配置:CentOS系统4核CPU8g内存jdk1.6.0_25,jvm配置-server-Xms2048m-Xmx2048m出现问题如下执行300并发,压测持续1个小时后内存使用率从20%上升到100%,tps从1100多降低到600多。排查问题的详细过程首先使用top命令查看内......
  • Java应用堆外内存泄露问题排查
    问题是怎么发现的最近有个java应用在做压力测试压测环境配置:CentOS系统4核CPU8g内存jdk1.6.0_25,jvm配置-server-Xms2048m-Xmx2048m出现问题如下执行300并发,压测持续1个小时后内存使用率从20%上升到100%,tps从1100多降低到600多。排查问题的详细过程首先使用top命令查......
  • .NET对象的内存布局
    在.NET中,理解对象的内存布局是非常重要的,这将帮助我们更好地理解.NET的运行机制和优化代码,本文将介绍.NET中的对象内存布局。.NET中的数据类型主要分为两类,值类型和引用类型。值类型包括了基本类型(如int、bool、double、char等)、枚举类型(enum)、结构体类型(struct),它们直接存储值。引......
  • AI芯片暴涨!沙特、阿联酋等国加入抢货行列 | 百能云芯
    在全球半导体市场中,一场异常激烈的竞争正在酝酿,引发了各国科技巨头和企业的争相购买英伟达AI芯片的浪潮。除了美国科技大厂之外,包括百度、字节跳动、阿里等中国企业在内,沙特阿拉伯与阿拉伯联合酋长国也纷纷加入了这场角逐,似乎每个人都想在人工智能领域抢先一步。在这场全球疯抢英伟......
  • 内存管理
    虚拟存储空间的大小受到计算机地址位宽因素限制。页面置换算法先进先出页面置换算法(FIFO)思想:总是把最先装入内存的一页调出会产生贝莱迪异常(BeladyAnomaly)现象,分配给进程的物理页面数增加时,缺页次数反而增加 最近最少使用页面置换算法(LRU)思想:选择距离现在最长时间内没有被访问的......
  • tonardo做web服务器播放大视频内存泄露问题的解决
    之前为了实现websocket来完成网页的推送,所以使用了tonardo作为web服务器。但是如果网页中含有视频插件的话,特别是经常要播放大视频的话,在linux环境下,经常发现python进程会莫名其妙的死掉。通过内存检测命令动态查看,发现python进程的内存占用一直居高不下,并且需要启用缓存才行。直到......
  • 代码性能测试 运行时间和占用内存
    运行时间用内置的`%time`和`%timeit`前者运行1次的时间,后者运行多次的平均值,放在单行代码前。要测试整个单元格,就是`%%time`和`%%timeit`,放在单元格的顶部。占用内存要用到第三方库memory_profiler,然后在单元格中导入 %load_extmemory_profiler在需要测量内存的代码单元格......
  • Linux下查看根目录各文件内存占用情况
    一、服务器运行一点时间后各种的项目文件,日志文件,数据库备份登,会越来越多,在linux下可以使用du和df命令查看。1、df-h 命令查看整体磁盘使用情况2、 使用 du-ah--max-depth=1  /  可以查看根目录下各个文件占用情况 使用命令du-h–max-depth=1/var/log......
  • 数据暴涨时代,该如何数据治理?_光点科技
    随着信息技术的迅猛发展,数据已经成为现代社会的核心资源。在这个被称为"数据暴涨时代"的时代里,大量的数据源源不断地被产生和积累,但如何有效地管理、分析和利用这些数据成为了一个迫切需要解决的问题。数据治理,作为数据管理的关键概念,在这个背景下显得尤为重要。定义数据治理的范围......
  • SocketAsyncEventArgs内存占用太大引发OutOfMemoryExceptions
    最近把一个接收socket服务端软件升级为iocp方案,其实就是换成c#的socket的异步方法,主要参考的是微软写的Server类,然后再百度找到基于这个类实现的代码来改造上线一两天后,软件崩溃了,查日志发现报了很多OutOfMemoryExceptions错误,都是在发送方法上。因为我发送方法用的SocketAsyncE......