C 编程/stdlib.h/malloc
在计算中,malloc
是一个用于执行动态内存分配的子程序。malloc
是标准库的一部分,并在 stdlib.h
标头文件中声明。
malloc
有许多实现,每种实现的性能都不同,具体取决于计算硬件和程序的编写方式。性能在执行时间和所需内存方面都有所不同。使用 malloc 分配的内存的指针最终必须传递给 free
子程序以释放内存,以避免内存泄漏。
C 编程语言以静态、自动或动态方式管理内存。静态持续时间的变量在主(固定)内存中分配,并在程序的整个生命周期中持续存在;自动持续时间的变量在堆栈上分配,并在函数调用和返回时出现和消失。对于静态持续时间和 C99 之前(允许可变长度自动数组[1])的自动持续时间变量,分配的大小需要是编译时常量。如果所需大小在运行时才知道(例如,如果从用户或磁盘文件读取任意大小的数据),那么使用固定大小的数据对象是不够的。
分配内存的生存期也是一个需要考虑的因素。静态持续时间和自动持续时间内存都不适合所有情况。自动分配的数据无法跨多个函数调用持续存在,而静态数据无论是否需要都会在程序的生命周期中持续存在。在许多情况下,程序员需要在管理分配内存的生存期方面有更大的灵活性。
通过使用动态内存分配可以避免这些限制,在动态内存分配中,内存以更明确(但也更灵活)的方式管理,通常是从堆中分配,堆是为此目的而构建的内存区域。在 C 语言中,库函数 malloc
用于在堆上分配一块内存。程序通过 malloc
返回的指针访问这块内存。当不再需要内存时,将指针传递给 free
,该函数释放内存以便可用于其他目的。
一些平台提供库调用,允许从 C 堆栈而不是堆进行运行时动态分配(例如,Unix alloca()
,[2] Microsoft Windows CRTL 的 malloca()
[3])。当调用函数结束时,这部分内存会自动释放。C99 标准的更改减少了对该功能的需求,该标准增加了对块范围的可变长度数组的支持,这些数组的大小在运行时确定。
malloc
函数是标准 C 中用于分配内存的函数之一。它与数组类似。它的函数原型是
void *malloc(size_t size);
它分配 size
字节的内存。如果分配成功,则返回指向内存块的指针。此指针保证适合对齐到任何类型(包括结构体等),否则返回 NULL 指针。
通过 malloc
分配的内存是持久的:它将继续存在,即使程序离开调用分配的范围,直到程序终止或程序员显式地释放内存。这是通过使用 free
子程序实现的。它的原型是
void free(void *pointer);
它释放 pointer
指向的内存块。pointer
必须是之前由 malloc
、calloc
或 realloc
返回的,并且 pointer
在传递给 free 后不能使用。特别是,通过 new 或 new[] 分配的内存不应传递给 free,并且未来自 malloc(例如堆栈变量)或已释放的指针不能发送到 free。对 NULL 指针调用 free 是安全的,没有影响。
创建 10 个 int 对象数组的标准方法
int array[10];
但是,如果希望动态地分配类似数组,则可以使用以下代码
/* Allocate space for an array with ten elements of type
int. */
int *ptr = (int *) malloc(10 * sizeof(int));
if (ptr == NULL) {
/* Memory could not be allocated, the program should
handle the error here as appropriate. */
} else {
/* Allocation succeeded. Do something. */
free(ptr); /* We are done with the int objects, and
free the associated pointer. */
ptr = NULL; /* The pointed-to-data must not be used again,
unless re-assigned by using malloc
again. */
}
malloc
返回空指针以指示没有可用内存,或发生阻止分配内存的其他错误。
由于 malloc
调用可能因内存不足而失败,因此定义一个在 malloc
失败时调用 malloc
并退出的宏通常很方便。一个可能的宏定义是
if(!(ptr=malloc(sizeof(int))))
{
fprintf(stderr,"Insufficient memory"); /* Prints the necessary error statement. */
exit(EXIT_FAILURE); /* Exits <code>malloc</code> as there is insufficient memory. */
}
为该宏给出的宏将是
#define MALLOC(p,s) /*Here p & s are 2 integer pointer & variable respectively. */ \
if(!((p)=malloc(s))) \
{ \
fprintf(stderr,"Insufficient memory"); /* Prints the necessary error statement. */ \
exit(EXIT_FAILURE); /* Exits <code>malloc</code> as there is insufficient memory. */ \
}
因此,上面的基本代码可以用一行替换
MALLOC(ptr,sizeof(int));
此示例显示了 malloc
的一个有用习惯用法
int *ptr = malloc(10 * sizeof(*ptr));
也就是说,不要将硬编码类型写入 malloc 的参数中,而是使用要分配指针内容的 sizeof
运算符。这可以确保分配左侧和右侧的类型在代码修改时永远不会不同步。
'C' 函数用于创建和返回大小为 m*n 的二维数组
int **create(int m, n)
{
int **p, i;
p = (int **)malloc(m*sizeof(int*));/* this will store base order of all the row in p */
for(i = 0; i < m; i++)
p[i] = (int *)malloc(n*sizeof(int));/* this will create m row of n elements */
return p;
}
malloc
返回一个 void 指针 (void *
),它表示它是一个指向未知数据类型的区域的指针。malloc
返回的特定指针类型缺乏是类型不安全的行为:malloc
根据字节数分配,而不是根据类型分配。这与 C++ 的 new 运算符不同,new 运算符返回的指针的类型取决于操作数。(参见 C 类型安全)。
可以将此指针“强制转换”(参见类型转换)为特定类型
int *ptr;
ptr = malloc(10 * sizeof(*ptr)); // Without a cast
ptr = (int*)malloc(10 * sizeof(int)); // With a cast
执行此类强制转换有优点和缺点。
- 包含强制转换可以与 C++ 兼容,C++ 要求进行强制转换。
- 如果强制转换存在,并且随后更改了左侧指针的类型,则会生成一个警告,以帮助程序员纠正否则可能出现错误的行为。
- 强制转换允许使用最初返回
char *
的旧版本malloc
。[4]
- 在 ANSI C 标准下,强制转换是多余的。
- 添加强制转换可能会掩盖未包含头文件
stdlib.h
的错误,该文件中包含malloc
的原型。[4][5] 在没有malloc
的原型的情况下,标准要求C编译器假设malloc
返回一个int
。如果没有强制转换,当将该整数赋值给指针时会发出警告;但是,使用强制转换后,不会产生此警告,从而隐藏了错误。在某些架构和数据模型(例如 64 位系统上的 LP64,其中long
和指针为 64 位,而int
为 32 位)上,此错误实际上会导致未定义行为,因为隐式声明的malloc
返回一个 32 位值,而实际定义的函数返回一个 64 位值。根据调用约定和内存布局,这可能会导致堆栈溢出。此问题在现代编译器中不存在,因为它们一致地生成有关使用未声明函数的警告,因此仍然会出现警告。例如,gcc 的默认行为是显示一个警告,提示“内置函数的隐式声明不兼容”,无论是否使用强制转换。
malloc
返回一个为程序员使用而分配的内存块,但未初始化。如果需要,通常会手动初始化内存——可以通过memset
函数,或者通过一个或多个取消引用指针的赋值语句。另一种方法是使用calloc
函数,该函数分配连续内存,然后将其初始化为零。其原型为
void *calloc(size_t nelements, size_t elementSize);
这将分配一个大小为nelements
× elementSize
的内存区域,并将其初始化为 0。当分配一个字符数组来保存字符串时,这很有用,如下面的示例所示
char *word = calloc(200, sizeof(char));
能够缩小或扩大内存块通常很有用。这可以通过使用realloc
来完成,它返回一个指向指定大小内存区域的指针,该区域包含与pointer
指向的旧区域相同的数据(截断为旧大小和新大小的最小值)。如果realloc
能够就地调整内存区域的大小,它将分配新的存储空间,复制所需的数据,并释放旧指针。如果此分配失败,realloc
将保持原始指针不变,并返回空指针值。在扩展的情况下,复制旧数据之外的新内存区域是未初始化的(内容不可预测)。函数原型为
void *realloc(void *pointer, size_t size);
如果pointer
为 NULL,则 realloc 的行为类似于给定size
的 malloc
void *p = malloc(42);
void *p = realloc(NULL, 42); /* equivalent */
在 C89 和 C99 中,长度为 0 的realloc
是一个特例。C89 标准明确规定给定的指针将被释放,返回值要么为空指针,要么是指向新分配空间的指针。C99 标准指出该行为是实现定义的。malloc 和 size 为 0 的 realloc 可能返回不同的(空和非空)指针。其他标准,例如 Open Group 的 UNIX 标准,使realloc(p, 0)
是否释放p
而无需分配新空间,或者可能释放p
并返回指向至少 0 字节内存的有效指针,成为实现定义的。在所有标准下,NULL 可以在内存分配失败时返回。使用realloc
时,应始终使用临时变量。例如
void *p = malloc(orig_size);
/* and later... */
void *tmp = realloc(p, big_size);
if (tmp != NULL) {
p = tmp; /* OK, assign new, larger storage to p */
} else {
/* handle the problem somehow */
}
如果相反地
void *p = malloc(orig_size);
/* and later... */
p = realloc(p, big_size);
并且如果无法获得big_size
字节的内存,那么 p 的值为 NULL,我们不再拥有指向先前为 p 分配的内存的指针,从而造成内存泄漏(见下文)。
malloc
和相关函数的错误使用经常会导致错误。
malloc
不能保证成功——如果内存不足,或者程序已经超过了它可以引用的内存量,malloc
将返回一个空指针,这应该始终在分配后检查。许多代码编写不好的程序没有检查malloc
是否失败。这样的程序将尝试使用malloc
返回的空指针,就好像它指向分配的内存一样。程序很可能会崩溃;在某些环境中,特别是没有执行虚拟内存管理的较旧或较小的平台上,零是一个有效地址,因此问题将不会被发现。
当malloc
、calloc
或realloc
的调用成功时,返回的指向分配内存的指针最终应该传递给free
函数。这将释放分配的内存,使其可以重用以满足其他内存分配请求。如果这样做没有完成,分配的内存将不会被释放,直到进程退出(在某些环境中,甚至在退出时也不会被释放)——换句话说,就会发生内存泄漏。通常,内存泄漏是由丢失指针造成的,例如没有使用临时指针来保存realloc
的返回值,这可能导致原始指针被空指针覆盖,例如
void *ptr;
size_t size = BUFSIZ;
ptr = malloc(size);
/* some further execution happens here... */
/* now the buffer size needs to be doubled */
if (size > SIZE_MAX / 2) {
/* handle overflow error */
/* . probably appropriate to use free( ptr ) here . */
return (1);
}
size *= 2;
ptr = realloc(ptr, size);
if (ptr == NULL) {
/* the realloc failed (it returned a null pointer), but
the original address in ptr has been lost so the
memory cannot be freed and a leak has occurred */
/* ... */
return 1;
}
/* ... */
在将指针传递给free
之后,它将成为一个悬挂指针:它引用一个具有未定义内容的内存区域,该区域可能不可用。无法访问指针的值。例如
int *ptr = malloc(sizeof (int));
free(ptr);
*ptr = 7; /* Undefined behavior */
这样的代码具有未定义的行为:其效果可能会有所不同。实际上,即使尝试读取已释放指针的值也会导致未定义行为(这里)。
通常,系统可能已经将释放的内存重用于其他目的。因此,通过指向已释放内存区域的指针写入可能会导致覆盖程序中其他地方的另一段数据。根据覆盖的数据,这可能会导致数据损坏,或者导致程序在以后的时间崩溃。此问题的特别糟糕的示例是如果将同一个指针两次传递给free
,这被称为双重释放。'使用后释放' 和 '双重释放' 错误会导致安全漏洞。[6] 为了避免这种情况,一些程序员在将指针传递给free
之后将它们设置为NULL
:[7]
free(ptr);
ptr = NULL; /*is safe (throws away the pointer's location).*/
但是,这不会保护对同一个指针的其他别名不被滥用。
另一个问题是,当free
传递一个未由malloc
、realloc
或calloc
分配的地址时。这可能是由于将指向文字字符串的指针或已声明数组的名称传递给free
造成的,例如
char *msg = "Default message";
int tbl[100];
将上述任一指针传递给free
会导致未定义的行为。
一个常见的错误是释放内存,然后使用它
char *ch_ptr = malloc(20);
for (i = 0; i < 19; i++) ch_ptr[i] = 'A';
i[19] = '\0';
free(ch_ptr);
printf("%s\n", ch_ptr);
这被称为“释放后使用”。这将在许多系统上运行。当free()
不改变被释放的内存的内容时,就会发生这种情况。C 的标准不保证这种行为。因此,它肯定会有一些系统无法运行。
当函数返回指向分配内存的指针时,通常的做法是将返回的指针放入一个变量中,使用内存,然后使用指针释放它
char *ch_ptr = malloc(20);
for (i = 0; i < 19; i++) ch_ptr[i] = 'A';
i[19] = '\0';
printf("%s\n", ch_ptr);
free(ch_ptr);
在某些程序中,内存块会被释放两次。这是因为混淆了哪个函数负责内存释放。例如
void main(){
int *p;
p = (int *)malloc(10 * sizeof(int));
f(p);
free(p);
}
void f(int *g){
printf("%d", g);
free(g);
}
这被称为“双重释放”或“多次释放”。这段代码在某些系统上可以运行,但在第二次运行时可能会崩溃释放.
内存管理的实现很大程度上取决于操作系统和架构。一些操作系统为 malloc 提供分配器,而另一些操作系统提供函数来控制某些数据区域。在 C++ 中,通常使用相同的动态内存分配器来实现 malloc
和 new
运算符 [需要引用]。因此,以下将其称为分配器,而不是 malloc
。
分配器在 IA-32 架构上的实现通常使用堆或数据段。分配器通常会扩展和收缩堆来满足分配请求。
堆方法存在一些固有的缺陷,完全源于碎片化。与任何内存分配方法一样,堆也会变得碎片化;也就是说,在堆上的已分配空间中,将存在已使用和未使用的内存部分。一个好的分配器会在诉诸扩展堆之前,尝试找到一个可用的已分配内存区域来使用。这种方法的主要问题是,堆只有两个重要的属性:基址,即堆在虚拟内存空间中的起点;以及长度,即其大小。堆需要足够的系统内存来填充其整个长度,并且其基址永远不会改变。因此,任何大块未使用的内存都被浪费了。如果在堆的末尾存在一个小的已使用段,则堆可能会“卡住”在这种位置,这可能会浪费任何数量的地址空间,从几兆字节到几百兆字节。
Doug Lea 是一个名为 dlmalloc(“Doug Lea 的 Malloc”)的内存分配器的作者,其源代码将自己描述为
“这不是有史以来编写速度最快、最节省空间、最便携或
最可调的 malloc。但它是最快的之一,同时也是最节省空间、最便携和最可调的之一。在这些因素之间取得一致的平衡,使其成为 malloc 密集型程序的良好通用分配器。”
dlmalloc 的第一个实现是在 1987 年创建的。 [8] 它用 C 编写,并且具有很高的可移植性,已知在所有主要的操作系统和处理器架构以及从中等/小型嵌入式系统到超级计算机的系统上都能很好地工作 [需要引用]。由于其长寿和开源性质,dlmalloc 被广泛用于教学目的以及作为其他分配器的基础,其中最著名的是 ptmalloc2/ptmalloc3。从 v2.3 版本开始,GNU C 库 (glibc) 使用一个修改过的 ptmalloc2,它本身基于 dlmalloc v2.7.0。 [9]
另一个鲜为人知的 dlmalloc 衍生产品是 nedmalloc,它基于 dlmalloc v2.8.4,本质上是 dlmalloc 被每个线程的旁路缓存包装,以提高执行并发性。
堆上的内存被分配为“块”,一个 8 字节对齐的数据结构,包含一个头和可用内存。已分配的内存包含 8 或 16 字节的开销,用于块的大小和使用标志。未分配的块还在可用空间区域存储指向其他空闲块的指针,这使得最小块大小为 24 字节。 [9]
未分配的内存被分组到大小相似的“bin”中,通过使用块的双向链表实现(指针存储在块内的未分配空间中)。 [9]
对于小于 256 字节的请求(“smallbin”请求),使用一个简单的二进制幂最佳拟合分配器。一个 CPU 特殊操作(在 GCC 上为 __builtin_clz())用于非常快速地找到第一个设置位,并为对应于该最高位位置的 bin 返回一个块。如果该 bin 中没有空闲块,则从下一个最高 bin 中拆分一个块。
对于大小为 256 字节或以上但低于 mmap 阈值的请求,即 dlmalloc 所谓的“largebin”请求,dlmalloc 的最新版本使用了一种就地位遍历算法。这基于第一个设置位后的每个位状态来遍历二叉树,现代乱序 CPU 可以非常有效地执行此操作,并且对已分配块的数量几乎不变。这种算法的一个很大的优势是,如果它找不到请求的大小,它会返回具有下一个最大大小的块,这使得将该块拆分为所需大小变得非常容易。
对于超过 mmap 阈值的请求,内存始终使用 mmap 系统调用分配。默认情况下,阈值为 256 KB(ptmalloc2 上为 1 MB),但可以通过调用 mallopt 函数更改。 [10] mmap 方法避免了在大型缓冲区到期后,小型分配被困在大型缓冲区末尾的问题,但始终分配整个内存页,在许多架构上,内存页的大小为 4096 字节。 [11]
如果需要分配小于阈值但小于可用空闲空间的额外内存,dlmalloc 可能会使用 brk()
调用 Linux 内核来增加堆的大小。增加堆的大小会增加最顶层的块(荒野块)的大小,该块始终未分配,并且由 malloc 特殊处理。 [9]
dlmalloc 具有一个相当弱的空闲空间段合并算法,主要是因为空闲空间合并往往非常慢,因为会导致 TLB 缓存耗尽。它在默认情况下每 (4096) 个 free() 操作调用一次,它通过迭代之前从系统请求的每个段来工作,这些段没有被系统连续返回。它尝试识别不包含已分配块的大范围内存,并将它的段分成两个,并将空闲内存返回给系统。如果 dlmalloc 是 VM 系统的唯一用户,则此算法效果很好,但是,如果 dlmalloc 与另一个分配器同时使用,则 dlmalloc 的空闲空间合并器可能无法正确识别释放空闲内存的机会。
glibc 对 ptmalloc2 的实现 在这种情况下与 dlmalloc 不同,因为它通常使用 mmap 从内核请求额外内存来分配 1Mb 对齐的块,其源代码将这些块称为arena。ptmalloc2 试图确保每个执行线程都有单独的 arena,从而允许内存分配器内的并发性。
ptmalloc3 通过使 dlmalloc 的 smallbin(如上所述)成为每个线程的,对 ptmalloc2 进行了显着改进。这允许 ptmalloc3 为较小的块提供无锁并发,同时仍然允许为 largebin 分配提供单独的 arena。超过 mmap 阈值的分配仍然完全通过 mmap() 路由。
nedmalloc 与 ptmalloc2 类似,支持多个每个线程的 arena,但它还为较小尺寸的块添加了单独的每个线程的旁路缓存,这避免了处理器序列化,类似于 ptmalloc3 的典型 C++ 使用模式。nedmalloc 与下面提到的 Hoard 一样,能够修补 Microsoft Windows 二进制文件,以在给定进程内用自身替换系统分配器。nedmalloc 的最新版本实现了用户模式页分配器,它替换了 mmap(),从而允许内存页保存在旁路缓存中,因此极大地提高了大型分配的速度。
dlmalloc、ptmalloc2、ptmalloc3 和 nedmalloc 都是根据开源许可证发布的,因此可以供学生学习。
从 FreeBSD 7.0 和 NetBSD 5.0 开始,旧的 malloc
实现 (phkmalloc) 被 Jason Evans 编写的 jemalloc 替换。这样做主要是因为 phkmalloc 在多线程方面缺乏可扩展性。为了避免锁争用,jemalloc 为每个 CPU 使用单独的“arena”。测量多线程应用程序中每秒分配次数的实验表明,这使其能够随着线程数量线性扩展,而对于 phkmalloc 和 dlmalloc,性能与线程数量成反比。 [12]
jemalloc 被用作 Firefox 3 beta4pre 及更高版本在 Windows 和 Linux 中的默认分配器,而不是操作系统提供的分配器,除了 Mac OS X。由于碎片化减少,这提高了性能并降低了内存消耗。
OpenBSD 中 malloc
函数的实现利用了 mmap
。对于大于一页大小的请求,整个分配都使用 mmap
获取;较小的尺寸从 malloc
在多个“桶页”中维护的内存池分配,这些桶页也使用 mmap
分配。在调用 free
时,内存被释放并使用 munmap
从进程地址空间取消映射。该系统旨在通过利用 OpenBSD 的 mmap
系统调用中实现的地址空间布局随机化和间隙页功能来提高安全性,并检测使用后释放错误,因为在释放后,大型内存分配将完全取消映射,进一步使用会导致段错误并终止程序。
Hoard 内存分配器是一个旨在实现可扩展内存分配性能的分配器。与 OpenBSD 的分配器类似,Hoard 专门使用 mmap
,但它以 64 千字节的块(称为超级块)管理内存。Hoard 的堆在逻辑上分为一个全局堆和多个每个处理器堆。此外,还有一个线程本地缓存,可以保存有限数量的超级块。通过仅从本地每个线程或每个处理器堆上的超级块分配,并将大部分空闲超级块移到全局堆中以便其他处理器重用,Hoard 保持低碎片化,同时实现接近线性的可扩展性,线程数量的增加。[13]
每个线程都有用于小型分配的本地存储。对于大型分配,可以使用 mmap 或 sbrk。 Tcmalloc 具有用于死线程本地存储的垃圾收集。对于多线程程序,TCmalloc 被认为比 glibc 的 ptmalloc 快两倍以上。[14][15]
操作系统内核需要像应用程序一样分配内存。但是,内核中 malloc
的实现通常与 C 库使用的实现有很大不同。例如,内存缓冲区可能需要符合 DMA 强制执行的特殊限制,或者内存分配函数可能从中断上下文调用。[16] 这需要与操作系统内核的虚拟内存子系统紧密集成的 malloc
实现。
在 Linux 和 Unix 中,kmalloc 和 kfree 在内核中提供了 malloc 和 free 的功能。在 Windows 驱动程序中,ExAllocatePoolWithTag、ExAllocatePoolWithQuotaTag 和 ExFreePoolWithTag 在内核模式下提供 malloc/free 语义功能。
malloc
可以分配的最大内存块大小取决于主机系统,尤其是物理内存的大小和操作系统的实现。理论上,最大数量应该是 size_t
类型可以容纳的最大值,它是一个与实现相关的无符号整数,表示内存区域的大小。最大值为 2CHAR_BIT*sizeof(size_t) − 1
,或 C99 标准中的常量 SIZE_MAX
。
- ↑ gnu.org 上的 gcc 手册,2008 年 12 月 14 日访问
- ↑ "alloca". Man.freebsd.org. 2006-09-05. Retrieved 2011-09-18.
- ↑
malloca()
MSDN Visual C++ 开发人员中心页面。2009 年 3 月 12 日访问 - ↑ a b 常见问题解答 > 解释... > 将 malloc 转换为其他类型,Cprogramming.com,2007 年 3 月 9 日访问
- ↑ comp.lang.c 常见问题解答列表 · 问题 7.7b,C-FAQ,2007 年 3 月 9 日访问
- ↑ "CWE - CWE-415:双重释放 (2.1)". Cwe.mitre.org. Retrieved 2011-09-18.
- ↑ The Open Group Base Specifications Issue 6,The Open Group,2007 年 3 月 9 日访问
- ↑ "A Memory Allocator". Gee.cs.oswego.edu. Retrieved 2011-09-18.
- ↑ a b c d Kaempf, Michel (2001). "Vudo malloc tricks". Phrack (57): 8. Retrieved 2009-04-29.
{{cite journal}}
: Cite has empty unknown parameters:|coauthors=
and|month=
(help) - ↑ "Malloc Tunable Parameters". GNU. Retrieved 2009-05-02.
- ↑ Sanderson, Bruce (2004-12-12). "RAM, Virtual Memory, Pagefile and all that stuff". Microsoft Help and Support.
- ↑ http://people.freebsd.org/~jasone/jemalloc/bsdcan2006/jemalloc.pdf
- ↑ http://www.cs.umass.edu/~emery/pubs/berger-asplos2000.pdf
- ↑ http://goog-perftools.sourceforge.net/doc/tcmalloc.html
- ↑ Mark Callaghan (2009-01-18). "High Availability MySQL: Double sysbench throughput with TCMalloc". Mysqlha.blogspot.com. Retrieved 2011-09-18.
- ↑ "kmalloc()/kfree() include/linux/slab.h". People.netfilter.org. Retrieved 2011-09-18.
- IEEE Std 1003.1 标准中 malloc 的定义
- glibc 分配器基础设计,作者:Doug Lea
- ptmalloc 主页,作者:Wolfram Gloger
- Hoard 主页,作者:Emery Berger
- nedmalloc 主页,作者:Niall Douglas
- jemalloc 主页,作者:Jason Evans
- TCMalloc 主页,由 Google 开发的高性能 malloc
- 简单的内存分配算法,来自 OSDEV 社区
- Hoard: 适用于多线程应用程序的可扩展内存分配器,作者:Emery Berger
- 可扩展的无锁动态内存分配,作者:Maged M. Michael
- 内存管理内幕 - 动态分配的选择、权衡和实现,作者:Jonathan Bartlett
- 内存减少 (GNOME) 维基页面,包含大量关于修复 malloc 的信息
- C99 标准草案,包括 TC1/TC2/TC3
- 一些关于 C 语言的有用参考资料
- ISO/IEC 9899 - 程序设计语言 - C