• 热门专题

理解glibcmalloc

作者:  发布日期:2016-07-22 21:53:19
Tag标签:理解glibcmalloc  
  • 本篇文章主要完成了对《Understanding glibc malloc》的翻译工作。限于本人翻译水平与专业技术水平(纯粹为了了解内存分配而翻),本文章必定会有很多不足之处,请大家见谅,也欢迎大家的指正!
    联系邮箱:974985526@qq.com


    堆内存是一个很有意思的领域,这样的问题:

    堆内存是如何从内核中分配的?
    内存管理效率怎样?
    它是由内核、库函数还是应用本身管理的?
    堆内存可以开发吗?

    我也困惑了很久,但是直到最近我才有时间去了解它。下面就让我来谈谈我的研究成果。开源社区提供了很多现成的内存分配器(memory allocators ):

    dlmalloc – General purpose allocator ptmalloc2 – glibc jemalloc – FreeBSD and Firefox tcmalloc – Google libumem – Solaris …

    每一种分配器都宣称自己快(fast)、可拓展(scalable )、效率高(memory efficient)!但是并非所有的分配器都适用于我们的应用。内存吞吐量大(memory hungry)的应用程序的性能很大程度上取决于内存分配器的性能。

    在这篇文章中,我将只谈论“glibc malloc”内存分配器。为了更好地理解“glibc malloc”,我会联系最近的源代码。

    历史:ptmalloc2基于dlmalloc开发,并添加了对多线程的支持,于2006年公布。在公布之后,ptmalloc2被整合到glibc源代码中,此后ptmalloc2所有的修改都直接提交到glibc的malloc部分去了。因此,ptmalloc2的源码和glibc的 malloc源码有很多不一致的地方。

    系统调用 线程处理 Example 输出分析 在主线程 malloc 之前 在主线程 malloc 之后 在主线程 free 之后 在thread1 malloc 之前 在thread1 malloc 之后 在thread1 free 之后 Arena Arena的数量 Multiple Arena Multiple Heaps Chunk Allocated chunk Free chunk Bins Fast Bin Unsorted Bin Small Bin Large Bin Top Chunk Last Remainder Chunk

    系统调用

    在之前的文章中提到过malloc的内部调用为brk或mmap。

    译者注:其中有一张关于虚拟地址空间分布的图片,我觉得很有助于本篇文章的理解,因此把它放在此处。

    这里写图片描述

    线程处理

    Linux的早期版本使用dlmalloc为默认内存分配器,但是因为ptmalloc2提供了多线程支持,所以Linux后来采用ptmalloc2为默认内存分配器。多线程支持可以提升内存分配器的性能,进而间接提升应用的性能。在dlmalloc中,当有两个线程同时调用malloc时,只有一个线程能够访问临界区(critical section):因为空闲列表数据结构(freelist data structure)被所有可用线程共享。正如此,dlmalloc内存分配器在多线程应用中颇为耗时,性能不好。而在ptmalloc2中,当有两个线程同时调用malloc时,内存均会得到立即分配:因为每个线程都维护着一个独立的堆段。因此空闲列表数据结构维护的堆也是独立的。这种为每个线程独立地维护堆和空闲列表数据结构的行为就称为per thread arena

    Example:

    /* Per thread arena example. */
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    void* threadFunc(void* arg) {
            printf('Before malloc in thread 1
    ');
            getchar();
            char* addr = (char*) malloc(1000);
            printf('After malloc and before free in thread 1
    ');
            getchar();
            free(addr);
            printf('After free in thread 1
    ');
            getchar();
    }
    
    int main() {
            pthread_t t1;
            void* s;
            int ret;
            char* addr;
    
            printf('Welcome to per thread arena example::%d
    ',getpid());
            printf('Before malloc in main thread
    ');
            getchar();
            addr = (char*) malloc(1000);
            printf('After malloc and before free in main thread
    ');
            getchar();
            free(addr);
            printf('After free in main thread
    ');
            getchar();
            ret = pthread_create(&t1, NULL, threadFunc, NULL);
            if(ret)
            {
                    printf('Thread creation error
    ');
                    return -1;
            }
            ret = pthread_join(t1, &s);
            if(ret)
            {
                    printf('Thread join error
    ');
                    return -1;
            }
            return 0;
    }

    输出分析:

    在主线程 malloc 之前:

    在如下的输出里我们可以看到,这里还 没有 堆段 也没有 线程栈,因为 thread1 还没有创建!

    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
    Welcome to per thread arena example::6501
    Before malloc in main thread
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
    08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    b7e05000-b7e07000 rw-p 00000000 00:00 0 
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

    在主线程 malloc 之后:

    在如下的输出中我们可以看到堆段产生在数据段(0804b000-0806c000)之上,这表明堆内存是通过更高级别的中断产生(也即brk中断)。此外,请注意,尽管用户只申请了1000字节的内存,但是实际产生了132KB的堆内存。这个连续的堆内存区域被称为arena。因为这个arena是被主线程建立的,因此称为main arena。接下来的申请会继续分配这个arena的132KB中剩余的部分,直到用尽。当用尽时,它可以通过更高级别的中断扩容(在扩容之后,top chunk的大小也随之调整以圈进这块额外的空间)。相应地,arena也可以在top chunk空间过大时缩小。

    注意:top chunk是一个arena中最多的chunk。有关top chunk的更多信息详见下述“top chunk”部分。

    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
    Welcome to per thread arena example::6501
    Before malloc in main thread
    After malloc and before free in main thread
    ...
    sploitfun@sploitfun-VirtualBox:~/lsploits/hof/ptmalloc.ppt/mthread$ cat /proc/6501/maps
    08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
    b7e05000-b7e07000 rw-p 00000000 00:00 0 
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

    在主线程 free 之后:

    在如下的输出结果中我们可以看出当分配的内存区域free掉时,其后的内存并不会立即释放给操作系统。分配的内存区域(1000B)仅仅是移交给了’glibc malloc’,把这段free掉的区域添加在了main arenas bin中(在glibc malloc中,空闲列表数据结构被称为bin)。随后当用户请求内存时,’glibc malloc’就不再从内核中申请新的堆区了,而是尝试在bin中找到空闲区块,除非实在找不到。

    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
    Welcome to per thread arena example::6501
    Before malloc in main thread
    After malloc and before free in main thread
    After free in main thread
    ...
    sploitfun@sploitfun-VirtualBox:~/lsploits/hof/ptmalloc.ppt/mthread$ cat /proc/6501/maps
    08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
    b7e05000-b7e07000 rw-p 00000000 00:00 0 
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

    在thread1 malloc 之前:

    在如下的输出中我们可以看见此时并没有thread1的堆段,但是其每个线程栈都已建立。

    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
    Welcome to per thread arena example::6501
    Before malloc in main thread
    After malloc and before free in main thread
    After free in main thread
    Before malloc in thread 1
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
    08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
    b7604000-b7605000 ---p 00000000 00:00 0 
    b7605000-b7e07000 rw-p 00000000 00:00 0          [stack:6594]
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

    在thread1 malloc 之后:

    在如下的输出结果中我们可以看出thread1的堆段建立在内存映射段区域(b7500000-b7521000,132KB),这也表明了堆内存是使用mmap系统调用产生的,而非同主线程一样使用sbrk系统调用。同样地,尽管用户只请求了1000B,1MB的堆内存还是被映射到了进程地址空间。在这1MB,只有132KB被设置了读写权限并成为该线程的堆内存。这段连续内存(132KB)被称为 thread arena。

    注意:当用户请求超过128KB大小并且此时arena中没有足够的空间来满足用户的请求时,内存将通过使用mmap系统调用(不再是sbrk)来分配而不论请求是发自main arena还是 thread arena。

    ploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
    Welcome to per thread arena example::6501
    Before malloc in main thread
    After malloc and before free in main thread
    After free in main thread
    Before malloc in thread 1
    After malloc and before free in thread 1
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
    08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
    b7500000-b7521000 rw-p 00000000 00:00 0 
    b7521000-b7600000 ---p 00000000 00:00 0 
    b7604000-b7605000 ---p 00000000 00:00 0 
    b7605000-b7e07000 rw-p 00000000 00:00 0          [stack:6594]
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

    在thread1 free 之后:

    在如下的输出结果中我们可以看出free掉的分配的内存区域这一过程并不会把堆内存归还给操作系统,而是仅仅是移交给了’glibc malloc’,然后添加在了thread arenas bin中。

    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
    Welcome to per thread arena example::6501
    Before malloc in main thread
    After malloc and before free in main thread
    After free in main thread
    Before malloc in thread 1
    After malloc and before free in thread 1
    After free in thread 1
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
    08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
    0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
    b7500000-b7521000 rw-p 00000000 00:00 0 
    b7521000-b7600000 ---p 00000000 00:00 0 
    b7604000-b7605000 ---p 00000000 00:00 0 
    b7605000-b7e07000 rw-p 00000000 00:00 0          [stack:6594]
    ...
    sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

    Arena

    Arena的数量:

    在如下的例子中,我们可以看见主线程包含main arena而thread 1包含它自有的thread arena。所以若不计线程的数量,在线程和arena之间是否存在一对一映射关系?当然不存在,部分极端的应用甚至运行比处理器核心的数量还多的线程,在这种情况下,每个线程都拥有一个arena开销过高且意义不大。因此,应用的Arena数量限制是基于系统的核心数的。

    For 32 bit systems:
         Number of arena = 2 * number of cores + 1.
    For 64 bit systems:
         Number of arena = 8 * number of cores + 1.

    Multiple Arena:

    举例而言:让我们来看一个运行在单核计算机上的32位操作系统上的多线程应用(4线程=主线程+3个用户线程)的例子。这里线程数量(4)大于核心数的二倍加一,因此在这种条件下,’glibc malloc’认定multiple arenas会被所有可用线程共享。那么它是如何共享的呢?

    当主线程第一次调用malloc时,已经建立的 main arena会被没有任何竞争地使用。 当thread 1 和 thread 2第一次调用malloc时,一块新的arena就被创建且会被没有任何竞争地使用。此时线程和arena之间有着一对一的映射关系。 当thread3第一次调用malloc时,arena的数量限制被计算出来。这里超过了arena的数量限制,因此尝试复用已经存在的arena(Main arena 或 Arena 1 或 Arena 2)。 复用:
    一旦遍历出可用arena,就开始自旋申请该arena的锁。 如果上锁成功(比如说main arena上锁成功),就将该arena返回用户。 如果查无可用arena,thread 3的malloc操作阻塞,直到有可用的arena为止。 当thread 3第二次调用malloc时,malloc会尝试使用上一次使用的arena(main arena)。当main arena可用时就用,否则thread 3就一直阻塞直至main arena被free掉。因此现在main arena实际上是被main thread和thread 3所共享。

    Multiple Heaps:

    在’glibc malloc’中主要发现了3种数据结构:
    heap_info -Heap Header- 一个thread arena可以有多个堆。每个堆都有自己的堆头。为什么需要多个堆?每个thread arena都只包含一个堆,但是当这个堆段空间耗尽时,新的堆(非连续区域)就会被mmap到这个aerna。
    malloc_state -Arena header- 一个thread arena可以有多个堆,但是所有这些堆只存在arena header。arena header包括的信息有:bins、top chunk、last remainder chunk…
    malloc_chunk -Chunk header- 一个堆根据用户请求被分为若干chunk。每个这样的chunk都有自己的chunk header。

    注意

    Main arena 没有多个堆,因此没有heap_info结构。当main arena空间耗尽时,就拓展sbrk’d堆段(拓展后是连续内存区域),直至碰到内存mapping区域为止。 不像thread arena,main arena的arena header不是sbrk’d堆段的一部分,而是一个全局变量,因此它可以在libc.so的 数据段中被找到。

    main arena和thread arena的图示如下(单堆段):
    main arena和thread arena
    thread arena的图示如下(多堆段):
    thread arena

    Chunk

    堆段中能找到的chunk类型如下:

    Allocated chunk Free chunk Top chunk Last Remainder chunk

    Allocated chunk:

    Allocated chunk

    prev_size:若上一个chunk可用,则此结构成员赋值为上一个chunk的大小;否则若上一个chunk被分配,此此结构成员赋值为上一个chunk的用户数据大小。
    size:此结构成员赋值为已分配chunk大小,其最后三位包含标志(flag)信息。

    PREV_INUSE (P) – 置“1”表示上一个chunk被分配; IS_MMAPPED (M) – 置“1”表示这一个chunk被nmap’d; NON_MAIN_ARENA (N) – 置“1”表示这一个chunk属于一个thread arena。

    注意

    malloc_chunk 中的其余结构成员,如fd、 bk不用于已分配的chunk,因此它们被拿来存储用户数据; 用户请求的大小被转换为可用大小(内部显示大小),因为需要一些额外的空间存储malloc_chunk,此外还需要考虑对齐的因素。

    Free chunk:

    Free chunk

    prev_size:两个空闲chunk不能毗连,而应合并成一个。因此前一个chunk和这一个空闲chunk都会被分配,此时prev_size中保存上一个chunk的用户数据。
    size:该结构成员保存本空闲chunk的大小。
    fd:Forward pointer - 指向同一bin中的下一个chunk(而非物理内存中下一块)。
    bk:Backward pointer – 指向同一bin中的上一个chunk(而非物理内存中上一块)。

    Bins

    Bins就是空闲列表数据结构。它们用以保存空闲chunk。基于chunk的大小,有下列几种可用bins:

    Fast bin Unsorted bin Small bin Large bin

    保存这些bin的数据结构为:

    fastbinsY:这个数组用以保存fast bins。
    bins:这个数组用以保存unsorted、small以及large bins,共计可容纳126个:

    Bin 1 – unsorted bin Bin 2 to Bin 63 – small bin Bin 64 to Bin 126 – large bin

    Fast Bin:

    大小为16~80字节的chunk被称为fast chunk。在所有的bin中,fast bin在内存分配以及释放上有更快的速度。

    数量 - 10
    每个fast bin都记录着一条free chunk的单链表(称为binlist ,采用单链表是出于fast bin中链表中部的chunk不会被摘除的特点),增删chunk都发生在链表的前端。 -LIFO 大小 - 8字节递增
    fast bins 记录着大小以8字节递增的bin链表。也即,fast bin(index 0)记录着大小为16字节的chunk的binlist、fast bin(index 1)记录着大小为24字节的chunk的binlist 依次类推…… 指定fast bin中所有chunk大小均相同。 在malloc初始化过程中,最大的fast bin的大小被设置为64而非80字节。因为默认情况下只有大小16~64的chunk被分类为fast chunk。 不能合并 - 两个毗连的空闲chunk并不会被合并成一个空闲chunk。不合并可能会导致碎片化问题,但是却可以大大加速释放的过程! malloc(fast chunk)
    初始情况下fast bin最大内存容量以及指针域均未初始化,因此即使用户请求fast chunk,服务的也将是small bin code而非fast bin code。 当它非空后,fast bin索引将被计算以检索对应binlist。 binlist中被检索的第一个个chunk将被摘除并返回给用户。 free(fast chunk)
    fast bin索引被计算以索引相应binlist。 free掉的chunk将被添加在索引到的binlist的前端。

    fast bin snapshot

    Unsorted Bin:

    当small或large chunk被free掉时,若非被添加到它们各自的bin中时,则被加在unsorted bin中。这种途径给予 ‘glibc malloc’ 第二次机会以重新使用最近free掉的chunk,这样寻找合适bin的时间开销就被抹掉了,因此内存的分配和释放会更快一些。

    数量 -1
    unsorted bin包括一个用于保存空闲chunk的双向循环链表(又名binlist)。

    chunk大小 - 无尺寸限制,任何大小chunk都可以添加进这里。

    unsorted,small and large bin snapshot

    Small Bin:

    大小小于512字节的chunk被称为small chunk,而保存small chunks的bin被称为small bin。small bin比large bin在内存分配回收的速度上更快。

    数量 -62

    每个small bin都包括一个空闲区块的双向循环链表(也称binlist)。free掉的chunk添加在链表的前端,而所需chunk则从链表后端摘除。-FIFO 大小 -8字节递增
    samll bins 记录着大小以8字节递增的bin链表。也即,第一个small bin(Bin 2)记录着大小为16字节的chunk的binlist、small bin(Bin 3)记录着大小为24字节的chunk的binlist 依次类推…… 指定samll bin中所有chunk大小均相同,因此无需排序。 合并 - 两个毗连的空闲chunk会被合并成一个空闲chunk。合并消除了碎片化的影响但是减慢了free的速度。 malloc(small chunk)
    初始情况下,small bin都会是NULL,因此尽管用户请求small chunk,提供服务的将是 unsorted bin code 而不是 small bin code。 同样地,在第一次调用malloc期间,在malloc_state找到的samll bin和large bin数据结构被初始化,bin都会指向它们本身以表示binlist为空。 此后当samll bin非空后,相应的bin会摘除binlist中最后一个chunk并返回给用户。 free(small chunk)
    在free一个chunk的时候,检查其前或其后的chunk是否空闲,若是则合并,也即把它们从所属的链表中摘除并合并成一个新的chunk,新chunk会添加在unsorted bin链表的前端。

    Large Bin:

    大小大于等于512字节的chunk被称为large chunk,而保存large chunks的bin被称为large bin。large bin比samll bin在内存分配回收的速度上更快。

    数量 -63
    每个large bin都包括一个空闲区块的双向循环链表(也称binlist)。free掉的chunk添加在链表的前端,而所需chunk则从链表后端摘除。-FIFO(?) 超过63个bin之后
    前32个bin记录着大小以64字节递增的bin链表,也即第一个large bin(Bin 65)记录着大小为512字节~568字节的chunk的binlist、第二个large bin(Bin 66)记录着大小为576字节到632字节的chunk的binlist,依次类推…… 后16个bin记录着大小以512字节递增的bin链表。 后8个bin记录着大小以4096字节递增的bin链表。 后4个bin记录着大小以32768字节递增的bin链表。 后2个bin记录着大小以262144字节递增的bin链表。 最后1个bin记录着大小为剩余大小的chunk。 不像samll bin,large bin中所有chunk大小不一定相同,因此各chunk需要递减保存。最大的chunk保存在最前的位置,而最小的chunk保存在最后的位置。 合并 - 两个毗连的空闲chunk会被合并成一个空闲chunk。 malloc(large chunk)
    初始情况下,large bin都会是NULL,因此尽管用户请求large chunk,提供服务的将是 next largetst bin code 而不是 large bin code。 同样地,在第一次调用malloc期间,在malloc_state找到的samll bin和large bin数据结构被初始化,bin都会指向它们本身以表示binlist为空。 此后当samll bin非空后,当最大chunk大小(在相应binlist中的)大于用户所请求的大小时,binlist就从顶部遍历到底部以找到一个大小最接近用户需求的chunk。一旦找到,相应chunk就会分成两块
    User chunk(用户请求大小)-返回给用户。 Remainder chunk(剩余大小) -添加到unsorted bin。 当最大chunk大小(在相应binlist中的)小于用户所请求的大小时,尝试在Next largest bin中查到到所需的chunk以响应用户请求。Next largest bin code会扫描binmaps以找到下一个最大非空bin,如果这样的bin找到了,就从其中的binlist中检索到合适的chunk并返回给用户;反之就使用top chunk以响应用户请求。 free(large chunk) -类似于small trunk。

    Top Chunk:

    一个arena中最顶部的chunk被称为Top Chunk。它不属于任何bin。在所有bin中都没有合适空闲内存区块的时候,才会使用top chunk来响应用户请求。当top chunk大小比用户所请求大小还大的时候,top chunk会分为两个部分:

    User chunk(用户请求大小) Remainder chunk(剩余大小)

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

    Last Remainder Chunk:

    最后一次小请求中因分割而得到的Remainder。Last Remainder Chunk有助于改进引用的局部性,也即连续的对small chunk的malloc请求可能最终导致各chunk被分配得彼此贴近。

    但是除了在一个 arena 里可用的的诸chunk,哪些chunk有资格成为Last Remainder Chunk呢?

    当一个用户请求small chunk而无法从samll bin和unsorted bin得到服务时,binmaps就会被扫描以找到下一个最大非空bin。正如前文所提及的,如果这样的bin找到了,它就会分割为两部分:返回给用户的user chunk、添加到unsorted bin中的remainder chunk。此外,这一remainder chunk还会成为最新的 last remainder chunk。

    那么参考局部性是如何实现的呢?

    现在当用户随后的请求是请求一块small chunk并且last remainder chunk是unsorted bin中唯一的chunk,last remainder chunk就分割成两部分:返回给用户的user chunk、添加到unsorted bin中的remainder chunk。此外,这一remainder chunk还会成为最新的 last remainder chunk。因此随后的内存分配最终导致各chunk被分配得彼此贴近。

延伸阅读:

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规