浅析malloc的底层实现原理(ptmalloc)

版权声明:本文为博主整理文章,未经博主允许不得转载。 https://blog.csdn.net/ZYZMZM_/article/details/90670471


内存布局

介绍ptmalloc之前,我们首先来了解一下X86 平台 Linux 进程内存布局,以32位系统为例:

从上图可以看到,栈至顶向下扩展,堆至底向上扩展mmap 映射区域至顶向下扩展。 mmap 映射区域和堆相对扩展,直至耗尽虚拟地址空间中的剩余区域,这种结构便于 C 运行时库使用 mmap 映射区域和堆进行内存分配。


brk(sbrk)和mmap函数

首先,linux系统向用户提供申请的内存有 brk(sbrk) 和 mmap 函数。下面我们先来了解一下这几个函数。


brk() 和 sbrk()

#include <unistd.h>
int brk( const void *addr )
void* sbrk ( intptr_t incr );

两者的作用是扩展heap的上界brk

brk()的参数设置为新的brk上界地址,成功返回1,失败返回0;

sbrk()的参数为申请内存的大小,返回heap新的上界brk的地址


mmap()

#include <sys/mman.h>
void *mmap(void *addr, size\_t length,
int prot, int flags, int fd, off\_t offset);

int munmap(void *addr, size_t length);

mmap的第一种用法是映射磁盘文件到内存中;第二种用法是匿名映射不映射磁盘文件,而向映射区申请一块内存

malloc使用的是mmap的第二种用法(匿名映射)。

munmap函数用于释放内存。


内存管理的一般方法

1、C风格的内存管理

  • malloc:该函数分配给定的字节数,并返回一个指向它们的指针。 如果没有足够的可用内存,那么它返回一个空指针。
  • free:该函数获得指向由 malloc 分配的内存片段的指针,并将其释放,以便以后的程序或操作系统使用(实际上,一些 malloc 实现只能将内存归还给程序,而无法将内存归还给操作系统)。

C 风格的内存管理程序主要实现 malloc()和 free()函数内存管理程序主要通过调用 brk() 或者 mmap()进程添加额外的虚拟内存。Doug Lea Malloc,ptmalloc,BSD malloc,Hoard,TCMalloc 都属于这一类内存管理程序。

基于 malloc() 的内存管理器仍然有很多缺点,不管使用的是哪个分配程序。对于那些需要保持长期存储的程序并不适合使用 malloc()来管理内存。如果有大量的不固定的内存引用,经常难以知道它们何时被释放。生存期局限于当前函数的内存非常容易管理,但是对于生存期超出该范围的内存来说,管理内存则困难得多。因为管理内存的问题,很多程序倾向于使用它们自己的内存管理规则。


2、池式内存管理

内存池是一种半内存管理方法。内存池帮助某些程序进行自动内存管理,这些程序会经历一些特定的阶段,而且每个阶段中都有分配给进程的特定阶段的内存。例如,很多网络服务器进程都会分配很多针对每个连接的内存——内存的最大生存期限为当前连接的存在期。例如:Apache 使用了池式内存(pooled memory),将其连接拆分为各个阶段,每个阶段都有自己的内存池。在结束每个阶段时,会一次释放所有内存。


3、引用计数

在引用计数中,所有共享的数据结构都有一个域来包含当前活动“引用”结构的次数。当向一个程序传递一个指向某个数据结构指针时,该程序会将引用计数增加 1。实质上,是在告诉数据结构,它正在被存储在多少个位置上。然后,当进程完成对它的使用后,该程序就会将引用计数减少 1。结束这个动作之后,它还会检查计数是否已经减到零。如果引用计数为零,那么它将释放内存


4、垃圾收集(Garbage collection)

垃圾收集是全自动地检测并移除不再使用的数据对象。垃圾收集器通常会在当可用内存减少到少于一个具体的阈值时运行。通常,它们以程序所知的可用的一组“基本”数据:栈数据、全局变量、寄存器等作为出发点。然后它们尝试去追踪通过这些数据连接到每一块数据。收集器找到的都是有用的数据;它没有找到的就是垃圾,可以被销毁并重新使用这些无用的数据。为了有效地管理内存,很多类型的垃圾收集器都需要知道数据结构内部指针的规划,所以,为了正确运行垃圾收集器,它们必须是语言本身的一部分。


malloc 实现原理

因为brk、sbrk、mmap都属于系统调用,若每次申请内存,都调用这三个,那么每次都会产生系统调用,影响性能;其次,这样申请的内存容易产生碎片,因为堆是从低地址到高地址,如果高地址的内存没有被释放,低地址的内存就不能被回收。
  
因此,malloc采用的是内存池的管理方式(ptmalloc),ptmalloc 采用边界标记法将内存划分成很多块,从而对内存的分配与回收进行管理。为了内存分配函数malloc的高效性,ptmalloc会预先向操作系统申请一块内存供用户使用当我们申请和释放内存的时候,ptmalloc会将这些内存管理起来,并通过一些策略来判断是否将其回收给操作系统。这样做的最大好处就是,使用户申请和释放内存的时候更加高效,避免产生过多的内存碎片。


ptmalloc简介

ptmalloc 实现了 malloc(),free()以及一组其它的函数。以提供动态内存管理的支持。分配器处在用户程序和内核之间,它响应用户的分配请求,向操作系统申请内存,然后将其返回给用户程序,为了保持高效的分配,分配器一般都会预先分配一块大于用户请求的内存,并通过某种算法管理这块内存,来满足用户的内存分配要求,用户释放掉的内存也并不是立即就返回给操作系统,相反,分配器会管理这些被释放掉的空闲空间,以应对用户以后的内存分配要求。

也就是说,分配器不但要管理已分配的内存块,还需要管理空闲的内存块,当响应用户分配要求时,分配器会首先在空闲空间中寻找一块合适的内存给用户,在空闲空间中找不到的情况下才分配一块新的内存。为实现一个高效的分配器,需要考虑很多的因素。比如,分配器本身管理内存块所占用的内存空间必须很小,分配算法必须要足够的快。


内存管理数据结构概述

主分配区与非主分配区

在 Doug Lea 实现的内存分配器中只有一个主分配区(main arena),每次分配内存都必须对主分配区加锁,分配完成后释放锁,在 SMP 多线程环境下,对主分配区的锁的争用很激烈,严重影响了 malloc 的分配效率。主分配区与非主分配区用环形链表进行管理。每一个分配区利用互斥锁(mutex)使线程对于该分配区的访问互斥。

每个进程只有一个主分配区,但可能存在多个非主分配区,ptmalloc 根据系统对分配的争用情况动态增加非主分配区的数量,分配区的数量一旦增加,就不会再减少了。主分配区可以访问进程的 heap 区域和 mmap 映射区域,也就是说主分配区可以使用 sbrk 和 mmap向操作系统申请虚拟内存。而非主分配区只能访问进程的 mmap 映射区域

主分配区可以访问 heap 区域,如果用户不调用 brk()或是 sbrk()函数,分配程序就可以保证分配到连续的虚拟地址空间,因为每个进程只有一个主分配区使用 sbrk()分配 heap 区域的虚拟内存。内核对 brk 的实现可以看着是 mmap 的一个精简版,相对高效一些。如果主分配区的内存是通过 mmap()向系统分配的,当 free 该内存时,主分配区会直接调用 munmap()将该内存归还给系统

当某一线程需要调用 malloc()分配内存空间时,该线程先查看线程私有变量中是否已经存在一个分配区,如果存在,尝试对该分配区加锁,如果加锁成功,使用该分配区分配内存,如果失败,该线程搜索循环链表试图获得一个没有加锁的分配区如果所有的分配区都已经加锁,那么 malloc()会开辟一个新的分配区,把该分配区加入到全局分配区循环链表并加锁,然后使用该分配区进行分配内存操作。在释放操作中,线程同样试图获得待释放内存块所在分配区的锁,如果该分配区正在被别的线程使用,则需要等待直到其他线程释放该分配区的互斥锁之后才可以进行释放操作。

申请小块内存时会产生很多内存碎片,ptmalloc 在整理时也需要对分配区做加锁操作。每个加锁操作大概需要 5~10 个 cpu 指令,而且程序线程很多的情况下,锁等待的时间就会延长,导致 malloc 性能下降。一次加锁操作需要消耗 100ns 左右,正是锁的缘故,导致 ptmalloc在多线程竞争情况下性能远远落后于 tcmalloc。


chunk组织

用户请求分配的空间在 ptmalloc 中都使用一个 chunk 来表示,用户调用 free()函数释放掉的内存也并不是立即就归还给操作系统,相反,它们也会被表示为一个 chunk,ptmalloc 使用特定的数据结构来管理这些空闲的 chunk。

ptmalloc 在给用户分配的空间的前后加上了一些控制信息,用这样的方法来记录分配的信息,以便完成分配和释放工作。一个使用中的 chunk(使用中,就是指还没有被 free 掉)在内存中如下图所示:

说明:

  • chunk指针指向chunk开始的地址mem指针指向用户内存块开始的地址
  • p=0时,表示前一个chunk为空闲,prev_size才有效
  • p=1时,表示前一个chunk正在使用,prev_size无效 p主要用于内存块的合并操作;ptmalloc 分配的第一个块总是将p设为1, 以防止程序引用到不存在的区域
  • M=1 为mmap映射区域分配;M=0为heap区域分配
  • A=0 为主分配区分配;A=1 为非主分配区分配。

空闲 chunk 在内存中的结构如下图所示:

说明:

  • 当chunk空闲时,其M状态是不存在的,只有AP状态
  • 原本是用户数据区的地方存储了四个指针
  • 指针fd指向后一个空闲的chunk,而bk指向前一个空闲的chunk,malloc通过这两个指针将大小相近的chunk连成一个双向链表
  • 在large bin中的空闲chunk,还有两个指针,fd_nextsize和bk_nextsize,用于加快在large bin中查找最近匹配的空闲chunk。不同的chunk链表又是通过bins或者fastbins来组织的。

空闲链表 bins

用户 free 掉的内存并不是都会马上归还给系统,ptmalloc 会统一管理 heap 和 mmap 映射区域中的空闲的 chunk,当用户进行下一次分配请求时,ptmalloc 会首先试图在空闲的chunk 中挑选一块给用户,这样就避免了频繁的系统调用,降低了内存分配的开销

ptmalloc 将相似大小的 chunk 用双向链表链接起来,这样的一个链表被称为一个 bin。ptmalloc 一共维护了 128 个 bin,每个bins都维护了大小相近的双向链表的chunk。基于chunk的大小,有下列几种可用bins:

  • Fast bin
  • Unsorted bin
  • Small bin
  • Large bin

ptmalloc 使用一个数组来存储这些 bin,如下图所示:

数组中的第一个为 unsorted bin,数组中从 2 开始编号的前 64 个 bin 称为 small bins同一个small bin中的chunk具有相同的大小。两个相邻的small bin中的chunk大小相差8bytes。small bins 中的 chunk 按照最近使用顺序进行排列(FIFO),最后释放的 chunk 被链接到链表的头部,而申请 chunk 是从链表尾部开始,这样,每一个 chunk 都有相同的机会被 ptmalloc 选中。

Small bins 后面的 bin 被称作 large bins。large bins 中的每一个 bin 分别包含了一个给定范围内的 chunk,其中的 chunk 按大小序排列。相同大小的 chunk 同样按照最近使用顺序排列。

ptmalloc 使用smallest-first,best-fit(以最小块优先,最佳适合)原则在空闲 large bins 中查找合适的 chunk。

当空闲的 chunk 被链接到 bin 中的时候,ptmalloc 会把表示该 chunk 是否处于使用中的标志 P 设为 0(注意,这个标志实际上处在下一个 chunk 中),同时 ptmalloc 还会检查它前后的 chunk 是否也是空闲的,如果是的话,ptmalloc 会首先把它们合并为一个大的 chunk,然后将合并后的 chunk 放到 unstored bin 中

要注意的是,并不是所有的 chunk 被释放后就立即被放到 bin 中。ptmalloc 为了提高分配的速度,会把一些小的的 chunk 先放到一个叫做fast bins 的容器内。


Fast Bins

一般的情况是,程序在运行时会经常需要申请和释放一些较小的内存空间。当分配器合并了相邻的几个小的 chunk 之后,也许马上就会有另一个小块内存的请求,这样分配器又需要从大的空闲内存中切分出一块,这样无疑是比较低效的,故而,ptmalloc 中在分配过程中引入了 fast bins,不大于 max_fast (默认值为 64B)的 chunk 被释放后,首先会被放到 fast bins 中,fast bins 中的 chunk 并不改变它的使用标志 P。这样也就无法将它们合并。

当需要给用户分配的 chunk 小于或等于 max_fast 时,ptmalloc 首先会在 fast bins 中查找相应的空闲块,然后才会去查找bins中的空闲chunk。在某个特定的时候,ptmalloc会遍历fast bins中的chunk,将相邻的空闲 chunk 进行合并,并将合并后的 chunk 加入 unsorted bin 中,然后再将 usorted bin 里的 chunk 加入 bins 中。


Unsorted Bin

unsorted bin 的队列在 bins 数组的第一个位置如果被用户释放的 chunk 大于 max_fast,或者 fast bins 中的空闲 chunk 合并后,这些 chunk 首先会被放到 unsorted bin 队列中

在进行 malloc 操作的时候,如果在 fast bins 中没有找到合适的 chunk,则 ptmalloc 会先在 unsorted bin 中查找合适的空闲 chunk,然后才查找 bins。如果 unsorted bin 不能满足分配要求。malloc 便会将 unsorted bin 中的 chunk 加入 bins 中。然后再从 bins 中继续进行查找和分配过程。从这个过程可以看出来,unsorted bin 可以看做是 bins 的一个缓冲区,增加它只是为了加快分配的速度。


small bins

大小 &lt; 512 &lt;512 字节的chunk被称为small chunk,而保存small chunks的bin被称为small bin。数组从2开始编号,前64个bin为small bins,small bin每个bin之间相差8个字节,同一个small bin中的chunk具有相同大小。

每个small bin都包括一个空闲区块的双向循环链表(也称binlist)。free掉的chunk添加在链表的前端,而所需chunk则从链表后端摘除

两个毗连的空闲chunk会被合并成一个空闲chunk。合并消除了碎片化的影响但是减慢了free的速度。

分配时,当samll bin非空后,相应的bin会摘除binlist中最后一个chunk并返回给用户。在free一个chunk的时候,检查其前或其后的chunk是否空闲,若是则合并,也即把它们从所属的链表中摘除并合并成一个新的chunk,新chunk会添加在unsorted bin链表的前端。


large bins

大小大 512 ≥512 字节的chunk被称为large chunk,而保存large chunks的bin被称为large bin,位于small bins后面。large bins中的每一个bin分别包含了一个给定范围内的chunk,其中的chunk按大小递减排序,大小相同则按照最近使用时间排列。

两个毗连的空闲chunk会被合并成一个空闲chunk。

ptmalloc 使用smallest-first,best-fit(以最小块优先,最佳适合)原则从顶部遍历到底部以找到一个大小最接近用户需求的chunk。一旦找到,相应chunk就会分成两块User chunk(用户请求大小)返回给用户


并不是所有chunk都按照上面的方式来组织,有三种例外情况。

  • top chunk
  • mmaped chunk
  • last remainder chunk

Top chunk

top chunk相当于分配区的顶部空闲内存,当bins上都不能满足内存分配要求的时候,就会来top chunk上分配。

当top chunk大小比用户所请求大小还大的时候,top chunk会分为两个部分:User chunk(用户请求大小)和Remainder chunk(剩余大小)。其中Remainder chunk成为新的top chunk。

当top chunk大小小于用户所请求的大小时,top chunk就通过sbrk(main arena)或mmap(thread arena)系统调用来扩容


mmaped chunk

当需要分配的 chunk 足够大(大于分配阀值,默认128K),而且 fast bins 和 bins 都不能满足要求,甚至 top chunk 本身也不能满足分配需求时,ptmalloc 会使用 mmap 来直接使用内存映射来将页映射到进程空间,则会放到mmaped chunk上

当释放mmaped chunk上的内存的时候会直接交还给操作系统。


Last remainder chunk

Last remainder chunk是另外一种特殊的chunk,就像top chunk和mmaped chunk一样,不会在任何bins中找到这种chunk。当需要分配一个small chunk,但在small bins中找不到合适的chunk,如果last remainder chunk的大小大于所需要的small chunk大小,last remainder chunk被分裂成两个chunk,其中一个chunk返回给用户,另一个chunk变成新的last remainder chunk


sbrk 与 mmap

在堆区中,start_brk 指向 heap 的开始,而 brk 指向 heap 的顶部。可以使用系统调用 brk() 和 sbrk() 来增加标识 heap 顶部的 brk 值,从而线性的增加分配给用户的 heap 空间。在使 malloc 之前,brk 的值等于 start_brk,也就是说 heap 大小为 0。

ptmalloc 在开始时,若请求的空间小于 mmap 分配阈值(mmap threshold,默认值为 128KB)时,主分配区会调用 sbrk()增加一块大小为 (128 KB + chunk_size) align 4KB 的空间作为 heap。非主分配区会调用 mmap 映射一块大小为 HEAP_MAX_SIZE(32 位系统上默认为 1MB,64 位系统上默认为 64MB)的空间作为 sub-heap。这就是前面所说的 ptmalloc 所维护的分配空间。

当用户请求内存分配时,首先会在这个区域内找一块合适的 chunk 给用户。当用户释放了 heap 中的 chunk 时,ptmalloc 又会使用 fastbins 和 bins 来组织空闲 chunk。以备用户的下一次分配

若需要分配的 chunk 大小小于 mmap分配阈值,而 heap 空间又不够,则此时主分配区会通过 sbrk()调用来增加 heap 大小非主分配区会调用 mmap 映射一块新的 sub-heap,也就是增加 top chunk 的大小,每次 heap 增加的值都会对齐到 4KB

当用户的请求超过 mmap 分配阈值,并且主分配区使用 sbrk()分配失败的时候,或是非主分配区在 top chunk 中不能分配到需要的内存时,ptmalloc 会尝试使用 mmap()直接映射一块内存到进程内存空间使用 mmap()直接映射的 chunk 在释放时直接解除映射,而不再属于进程的内存空间。任何对该内存的访问都会产生段错误。而在 heap 中或是 sub-heap 中分配的空间则可能会留在进程内存空间内,还可以再次引用(当然是很危险的)。


内存分配概述

分配算法概述,以 32 系统为例,64 位系统类似。

  • 小于等于 64 字节:用 pool 算法分配。
  • 64 到 512 字节之间:在最佳匹配算法分配和 pool 算法分配中取一种合适的。
  • 大于等于 512 字节:用最佳匹配算法分配。
  • 大于等于 mmap 分配阈值(默认值 128KB):根据设置的 mmap 的分配策略进行分配,
  • 如果没有开启 mmap 分配阈值的动态调整机制,大于等于 128KB 就直接调用 mmap分配。否则,大于等于 mmap 分配阈值时才直接调用 mmap()分配。

ptmalloc 的响应用户内存分配要求的具体步骤为:

  1. 获取分配区的锁,防止多线程冲突。
  2. 计算出实际需要分配的内存的chunk实际大小。
  3. 判断chunk的大小,如果小于max_fast(64B),则尝试去fast bins上取适合的chunk,如果有则分配结束。否则,下一步;
  4. 判断chunk大小是否小于512B,如果是,则从small bins上去查找chunk,如果有合适的,则分配结束。否则下一步;
  5. ptmalloc首先会遍历 fast bins 中的chunk,将相邻的chunk进行合并,并链接到unsorted bin 中然后遍历 unsorted bins。如果 unsorted bins 上只有一个chunk并且大于待分配的chunk,则进行切割,并且剩余的chunk继续扔回 unsorted bins;如果 unsorted bins 上有大小和待分配chunk相等的,则返回,并从 unsorted bins删除;如果 unsorted bins 中的某一chunk大小属于 small bins 的范围,则放入small bins的头部;如果 unsorted bins 中的某一chunk大小属于 large bins 的范围,则找到合适的位置放入。若未分配成功,转入下一步;
  6. 从 large bins 中查找找到合适的chunk之后,然后进行切割,一部分分配给用户,剩下的放入 unsorted bin 中。
  7. 如果搜索 fast bins 和 bins 都没有找到合适的chunk,那么就需要操作top chunk来进行分配了。当 top chunk 大小比用户所请求大小还大的时候,top chunk会分为两个部分:User chunk(用户请求大小)和 Remainder chunk(剩余大小)。其中 Remainder chunk 成为新的 top chunk。 当 top chunk 大小小于用户所请求的大小时,top chunk就通过sbrk(main arena)或mmap(thread arena)系统调用来扩容。
  8. 到了这一步,说明 top chunk 也不能满足分配要求,所以,于是就有了两个选择: 如果是主分配区,调用 sbrk(),增加 top chunk 大小;如果是非主分配区,调用 mmap 来分配一个新的 sub-heap,增加 top chunk 大小;或者使用 mmap()来直接分配。在这里,需要依靠 chunk 的大小来决定到底使用哪种方法。判断所需分配的 chunk 大小是否大于等于 mmap 分配阈值,如果是的话,则转下一步,调用 mmap 分配, 否则跳到第 10 步,增加 top chunk 的大小。
  9. 使用 mmap 系统调用为程序的内存空间映射一块 chunk_size align 4kB 大小的空间。 然后将内存指针返回给用户。
    10.判断是否为第一次调用 malloc,若是主分配区,则需要进行一次初始化工作,分配一块大小为(chunk_size + 128KB) align 4KB 大小的空间作为初始的 heap。若已经初始化过了,主分配区则调用 sbrk()增加 heap 空间,分主分配区则在 top chunk 中切 割出一个 chunk,使之满足分配需求,并将内存指针返回给用户。

简而言之: 获取分配区(arena)并加锁 fast bin unsorted bin small bin large bin top chunk 扩展堆


内存回收概述

free() 函数接受一个指向分配区域的指针作为参数,释放该指针所指向的 chunk

而具体的释放方法则根据该 chunk 所处的位置和该 chunk 的大小而定。free()函数的工作步骤如下:

  1. 获取分配区的锁,保证线程安全。
  2. 如果free的是空指针,则返回,什么都不做。
  3. 判断当前 chunk 是否是 mmap 映射区域映射的内存,如果是,则直接munmap()释放这块内存。前面的已使用chunk的数据结构中,我们可以看到有M来标识是否是mmap映射的内存
  4. 判断 chunk 是否与 top chunk 相邻,如果相邻,则直接和top chunk合并(和top chunk相邻相当于和分配区中的空闲内存块相邻)。转到步骤8
  5. 如果chunk的大小大于max_fast(64b),则放入unsorted bin,并且检查是否有合并,有合并情况并且和 top chunk 相邻,则转到步骤8;没有合并情况则free。
  6. 如果chunk的大小小于 max_fast(64b),则直接放入fast bin,fast bin并没有改变chunk的状态。没有合并情况,则free;有合并情况,转到步骤7
  7. 在fast bin,如果当前chunk的下一个chunk也是空闲的,则将这两个chunk合并,放入unsorted bin上面。合并后的大小如果大于64B,会触发进行fast bins的合并操作,fast bins中的chunk将被遍历,并与相邻的空闲chunk进行合并,合并后的chunk会被放到unsorted bin中,fast bin会变为空。合并后的chunk和topchunk相邻,则会合并到topchunk中。转到步骤8
  8. 判断 top chunk 的大小是否大于 mmap 收缩阈值(默认为128KB),如果是的话,对于主分配区,则会试图归还 top chunk 中的一部分给操作系统。free结束。

使用注意事项

为了避免Glibc内存暴增,需要注意:

  1. 后分配的内存先释放,因为ptmalloc收缩内存是从top chunk开始,如果与top chunk相邻的chunk不能释放,top chunk以下的chunk都无法释放。
  2. ptmalloc不适合用于管理长生命周期的内存,特别是持续不定期分配和释放长生命周期的内存,这将导致ptmalloc内存暴增。
  3. 不要关闭 ptmalloc 的 mmap 分配阈值动态调整机制,因为这种机制保证了短生命周期的 内存分配尽量从 ptmalloc 缓存的内存 chunk 中分配,更高效,浪费更少的内存。
  4. 多线程分阶段执行的程序不适合用ptmalloc,这种程序的内存更适合用内存池管理 。
  5. 尽量减少程序的线程数量和避免频繁分配/释放内存。频繁分配,会导致锁的竞争,最终导致非主分配区增加,内存碎片增高,并且性能降低。
  6. 防止内存泄露,ptmalloc对内存泄露是相当敏感的,根据它的内存收缩机制,如果与top chunk相邻的那个chunk没有回收,将导致top chunk一下很多的空闲内存都无法返回给操作系统。
  7. 防止程序分配过多的内存,或是由于glibc内存暴增,导致系统内存耗尽,程序因为OOM被系统杀掉。预估程序可以使用的最大物理内存的大小,配置系统的/proc/sys/vm/overcommit_memory ,/proc/sys/vm/overcommit_ratio,以及使用ulimit -v限制程序能使用的虚拟内存的大小,防止程序因OOM被杀死掉。

参考:
华庭 《Glibc 内存管理——Ptmalloc2 源代码分析》
《ptmalloc代码浅析2(small bin/large bin结构图)》
《malloc的底层实现(ptmalloc)》

猜你喜欢

转载自blog.csdn.net/ZYZMZM_/article/details/90670471