内存管理内幕
时间:2009-05-06 来源:mumutouv
- 确定您是否有足够的内存来处理数据。
- 从可用的内存中获取一部分内存。
- 向可用内存池(pool)中返回部分内存,以使其可以由程序的其他部分或者其他程序使用。
- malloc:该函数分配给定的字节数,并返回一个指向它们的指针。如果没有足够的可用内存,那么它返回一个空指针。
- free:该函数获得指向由 malloc 分配的内存片段的指针,并将其释放,以便以后的程序或操作系统使用(实际上,一些 malloc 实现只能将内存归还给程序,而无法将内存归还给操作系统)。
- brk: brk() 是一个非常简单的系统调用。还记得系统中断点吗?该位置是进程映射的内存边界。 brk() 只是简单地将这个位置向前或者向后移动,就可以向进程添加内存或者从进程取走内存。
- mmap: mmap(),或者说是“内存映像”,类似于 brk(),但是更为灵活。首先,它可以映射任何位置的内存,而不单单只局限于进程。其次,它不仅可以将虚拟地址映射到物理的 RAM 或者 swap,它还可以将它们映射到文件和文件位置,这样,读写内存将对文件中的数据进行读写。不过,在这里,我们只关心 mmap 向进程添加被映射的内存的能力。 munmap() 所做的事情与 mmap() 相反。
- void *malloc(long numbytes):该函数负责分配 numbytes 大小的内存,并返回指向第一个字节的指针。
- void free(void *firstbyte):如果给定一个由先前的 malloc 返回的指针,那么该函数会将分配的空间归还给进程的“空闲空间”。
清单 1. 我们的简单分配程序的全局变量
int has_initialized = 0; void *managed_memory_start; void *last_valid_address;
|
清单 2. 分配程序初始化函数
/* Include the sbrk function */ #include <unistd.h> void malloc_init() { /* grab the last valid address from the OS */ last_valid_address = sbrk(0); /* we don't have any memory to manage yet, so *just set the beginning to be last_valid_address */managed_memory_start = last_valid_address; /* Okay, we're initialized and ready to go */ has_initialized = 1; }
|
清单 3. 内存控制块结构定义
struct mem_control_block { int is_available; int size; };
|
在讨论分配内存之前,我们将先讨论释放,因为它更简单。为了释放内存,我们必须要做的惟一一件事情就是,获得我们给出的指针,回退 sizeof(struct mem_control_block) 个字节,并将其标记为可用的。这里是对应的代码:
清单 4. 解除分配函数
void free(void *firstbyte) { struct mem_control_block *mcb; /* Backup from the given pointer to find the * mem_control_block */mcb = firstbyte - sizeof(struct mem_control_block); /* Mark the block as being available */ mcb->is_available = 1; /* That's It! We're done. */ return; }
|
清单 5. 主分配程序的伪代码
1. If our allocator has not been initialized, initialize it. 2. Add sizeof(struct mem_control_block) to the size requested. 3. start at managed_memory_start. 4. Are we at last_valid address? 5. If we are:A. We didn't find any existing space that was large enough -- ask the operating system for more and return that. 6. Otherwise:A. Is the current space available (check is_available from the mem_control_block)? B. If it is: i) Is it large enough (check "size" from the mem_control_block)?ii) If so: a. Mark it as unavailableb. Move past mem_control_block and return the pointeriii) Otherwise: a. Move forward "size" bytes b. Go back go step 4C. Otherwise: i) Move forward "size" bytes ii) Go back to step 4
|
清单 6. 主分配程序
void *malloc(long numbytes) { /* Holds where we are looking in memory */ void *current_location; /* This is the same as current_location, but cast to a * memory_control_block */struct mem_control_block *current_location_mcb; /* This is the memory location we will return. It will * be set to 0 until we find something suitable */void *memory_location; /* Initialize if we haven't already done so */ if(! has_initialized) { malloc_init(); }/* The memory we search for has to include the memory * control block, but the users of malloc don't need * to know this, so we'll just add it in for them. */numbytes = numbytes + sizeof(struct mem_control_block); /* Set memory_location to 0 until we find a suitable * location */memory_location = 0; /* Begin searching at the start of managed memory */ current_location = managed_memory_start; /* Keep going until we have searched all allocated space */ while(current_location != last_valid_address) {/* current_location and current_location_mcb point * to the same address. However, current_location_mcb* is of the correct type, so we can use it as a struct. * current_location is a void pointer so we can use it * to calculate addresses. */current_location_mcb = (struct mem_control_block *)current_location; if(current_location_mcb->is_available) {if(current_location_mcb->size >= numbytes) { /* Woohoo! We've found an open, * appropriately-size location. */ /* It is no longer available */ current_location_mcb->is_available = 0; /* We own it */ memory_location = current_location; /* Leave the loop */ break; } }/* If we made it here, it's because the Current memory * block not suitable; move to the next one */current_location = current_location + current_location_mcb->size; }/* If we still don't have a valid location, we'll * have to ask the operating system for more memory */if(! memory_location) {/* Move the program break numbytes further */ sbrk(numbytes);/* The new memory will be where the last valid * address left off */memory_location = last_valid_address; /* We'll move the last valid address forward * numbytes */last_valid_address = last_valid_address + numbytes; /* We need to initialize the mem_control_block */ current_location_mcb = memory_location; current_location_mcb->is_available = 0; current_location_mcb->size = numbytes; }/* Now, no matter what (well, except for error conditions), * memory_location has the address of the memory, including * the mem_control_block *//* Move the pointer past the mem_control_block */ memory_location = memory_location + sizeof(struct mem_control_block); /* Return the pointer */ return memory_location; }
|
清单 7. 编译分配程序
gcc -shared -fpic malloc.c -o malloc.so
|
清单 8. 替换您的标准的 malloc
LD_PRELOAD=/path/to/malloc.so export LD_PRELOAD
|
- 由于它对系统中断点(一个全局变量)进行操作,所以它不能与其他分配程序或者 mmap 一起使用。
- 当分配内存时,在最坏的情形下,它将不得不遍历 全部进程内存;其中可能包括位于硬盘上的很多内存,这意味着操作系统将不得不花时间去向硬盘移入数据和从硬盘中移出数据。
- 没有很好的内存不足处理方案( malloc 只假定内存分配是成功的)。
- 它没有实现很多其他的内存函数,比如 realloc()。
- 由于 sbrk() 可能会交回比我们请求的更多的内存,所以在堆(heap)的末端会遗漏一些内存。
- 虽然 is_available 标记只包含一位信息,但它要使用完整的 4-字节 的字。
- 分配程序不是线程安全的。
- 分配程序不能将空闲空间拼合为更大的内存块。
- 分配程序的过于简单的匹配算法会导致产生很多潜在的内存碎片。
- 我确信还有很多其他问题。这就是为什么它只是一个例子!
- 分配的速度。
- 回收的速度。
- 有线程的环境的行为。
- 内存将要被用光时的行为。
- 局部缓存。
- 簿记(Bookkeeping)内存开销。
- 虚拟内存环境中的行为。
- 小的或者大的对象。
- 实时保证。
- Doug Lea Malloc:Doug Lea Malloc 实际上是完整的一组分配程序,其中包括 Doug Lea 的原始分配程序,GNU libc 分配程序和 ptmalloc。 Doug Lea 的分配程序有着与我们的版本非常类似的基本结构,但是它加入了索引,这使得搜索速度更快,并且可以将多个没有被使用的块组合为一个大的块。它还支持缓存,以便更快地再次使用最近释放的内存。 ptmalloc 是 Doug Lea Malloc 的一个扩展版本,支持多线程。在本文后面的 参考资料部分中,有一篇描述 Doug Lea 的 Malloc 实现的文章。
- BSD Malloc:BSD Malloc 是随 4.2 BSD 发行的实现,包含在 FreeBSD 之中,这个分配程序可以从预先确实大小的对象构成的池中分配对象。它有一些用于对象大小的 size 类,这些对象的大小为 2 的若干次幂减去某一常数。所以,如果您请求给定大小的一个对象,它就简单地分配一个与之匹配的 size 类。这样就提供了一个快速的实现,但是可能会浪费内存。在 参考资料部分中,有一篇描述该实现的文章。
- Hoard:编写 Hoard 的目标是使内存分配在多线程环境中进行得非常快。因此,它的构造以锁的使用为中心,从而使所有进程不必等待分配内存。它可以显著地加快那些进行很多分配和回收的多线程进程的速度。在 参考资料部分中,有一篇描述该实现的文章。
众 多可用的分配程序中最有名的就是上述这些分配程序。如果您的程序有特别的分配需求,那么您可能更愿意编写一个定制的能匹配您的程序内存分配方式的分配程 序。不过,如果不熟悉分配程序的设计,那么定制分配程序通常会带来比它们解决的问题更多的问题。要获得关于该主题的适当的介绍,请参阅 Donald Knuth 撰写的 The Art of Computer Programming Volume 1: Fundamental Algorithms 中的第 2.5 节“Dynamic Storage Allocation”(请参阅 参考资料中的链接)。它有点过时,因为它没有考虑虚拟内存环境,不过大部分算法都是基于前面给出的函数。
在 C++ 中,通过重载 operator new(),您可以以每个类或者每个模板为单位实现自己的分配程序。在 Andrei Alexandrescu 撰写的 Modern C++ Design 的第 4 章(“Small Object Allocation”)中,描述了一个小对象分配程序(请参阅 参考资料中的链接)。
不只是我们的内存管理器有缺点,基于 malloc() 的内存管理器仍然也有很多缺点,不管您使用的是哪个分配程序。对于那些需要保持长期存储的程序使用 malloc() 来 管理内存可能会非常令人失望。如果您有大量的不固定的内存引用,经常难以知道它们何时被释放。生存期局限于当前函数的内存非常容易管理,但是对于生存期超 出该范围的内存来说,管理内存则困难得多。而且,关于内存管理是由进行调用的程序还是由被调用的函数来负责这一问题,很多 API 都不是很明确。
这 样做的好处是,您不必追踪程序中某个给定的数据结构可能会遵循的每一条路径。每次对其局部的引用,都将导致计数的适当增加或减少。这样可以防止在使用数据 结构时释放该结构。不过,当您使用某个采用引用计数的数据结构时,您必须记得运行引用计数函数。另外,内置函数和第三方的库不会知道或者可以使用您的引用 计数机制。引用计数也难以处理发生循环引用的数据结构。
清单 9. 基本的引用计数函数
/* Structure Definitions*/ /* Base structure that holds a refcount */ struct refcountedstruct {int refcount; }/* All refcounted structures must mirror struct * refcountedstruct for their first variables */ /* Refcount maintenance functions */ /* Increase reference count */ void REF(void *data) {struct refcountedstruct *rstruct; rstruct = (struct refcountedstruct *) data; rstruct->refcount++; } /* Decrease reference count */ void UNREF(void *data) {struct refcountedstruct *rstruct; rstruct = (struct refcountedstruct *) data; rstruct->refcount--;/* Free the structure if there are no more users */ if(rstruct->refcount == 0) { free(rstruct); } }
|
- UNREF 分配前左端指针(left-hand-side pointer)指向的值。
- REF 分配后左端指针(left-hand-side pointer)指向的值。
- 在函数的起始处 REF 每一个指针。
- 在函数的结束处 UNREF 第一个指针。
清单 10. 使用引用计数的示例
/* EXAMPLES OF USAGE */ /* Data type to be refcounted */ struct mydata { int refcount; /* same as refcountedstruct */ int datafield1; /* Fields specific to this struct */ int datafield2; /* other declarations would go here as appropriate */ }; /* Use the functions in code */ void dosomething(struct mydata *data) { REF(data);/* Process data */ /* when we are through */ UNREF(data); } struct mydata *globalvar1;/* Note that in this one, we don't decrease the * refcount since we are maintaining the reference * past the end of the function call through the * global variable */ void storesomething(struct mydata *data) {REF(data); /* passed as a parameter */ globalvar1 = data; REF(data); /* ref because of Assignment */ UNREF(data); /* Function finished */ }
|
- 实现简单。
- 易于使用。
- 由于引用是数据结构的一部分,所以它有一个好的缓存位置。
- 要求您永远不要忘记调用引用计数函数。
- 无法释放作为循环数据结构的一部分的结构。
- 减缓几乎每一个指针的分配。
- 尽管所使用的对象采用了引用计数,但是当使用异常处理(比如 try 或 setjmp()/ longjmp())时,您必须采取其他方法。
- 需要额外的内存来处理引用。
- 引用计数占用了结构中的第一个位置,在大部分机器中最快可以访问到的就是这个位置。
- 在多线程环境中更慢也更难以使用。
在池式内存管理中,每次内存分配都会指定内存池,从中分配内存。每个内存池都有不同的生存期限。在 Apache 中, 有一个持续时间为服务器存在期的内存池,还有一个持续时间为连接的存在期的内存池,以及一个持续时间为请求的存在期的池,另外还有其他一些内存池。因此, 如果我的一系列函数不会生成比连接持续时间更长的数据,那么我就可以完全从连接池中分配内存,并知道在连接结束时,这些内存会被自动释放。另外,有一些实 现允许注册 清除函数(cleanup functions),在清除内存池之前,恰好可以调用它,来完成在内存被清理前需要完成的其他所有任务(类似于面向对象中的析构函数)。
清单 11. obstack 的示例代码
#include <obstack.h> #include <stdlib.h> /* Example code listing for using obstacks */ /* Used for obstack macros (xmalloc isa malloc function that exits if memory is exhausted */ #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free /* Pools *//* Only permanent allocations should go in this pool */ struct obstack *global_pool;/* This pool is for per-connection data */ struct obstack *connection_pool; /* This pool is for per-request data */ struct obstack *request_pool; void allocation_failed() { exit(1); } int main() {/* Initialize Pools */ global_pool = (struct obstack *) xmalloc (sizeof (struct obstack)); obstack_init(global_pool);connection_pool = (struct obstack *) xmalloc (sizeof (struct obstack)); obstack_init(connection_pool);request_pool = (struct obstack *) xmalloc (sizeof (struct obstack)); obstack_init(request_pool);/* Set the error handling function */ obstack_alloc_failed_handler = &allocation_failed; /* Server main loop */ while(1) { wait_for_connection();/* We are in a connection */ while(more_requests_available()) {/* Handle request */ handle_request(); /* Free all of the memory allocated * in the request pool */ obstack_free(request_pool, NULL); }/* We're finished with the connection, time * to free that pool */obstack_free(connection_pool, NULL); } } int handle_request() {/* Be sure that all object allocations are allocated * from the request pool */int bytes_i_need = 400; void *data1 = obstack_alloc(request_pool, bytes_i_need); /* Do stuff to process the request */ /* return */ return 0; }
|
- 应用程序可以简单地管理内存。
- 内存分配和回收更快,因为每次都是在一个池中完成的。分配可以在 O(1) 时间内完成,释放内存池所需时间也差不多(实际上是 O(n) 时间,不过在大部分情况下会除以一个大的因数,使其变成 O(1))。
- 可以预先分配错误处理池(Error-handling pools),以便程序在常规内存被耗尽时仍可以恢复。
- 有非常易于使用的标准实现。
- 内存池只适用于操作可以分阶段的程序。
- 内存池通常不能与第三方库很好地合作。
- 如果程序的结构发生变化,则不得不修改内存池,这可能会导致内存管理系统的重新设计。
- 您必须记住需要从哪个池进行分配。另外,如果在这里出错,就很难捕获该内存池。
垃圾收集(Garbage collection)是全自动地检测并移除不再使用的数据对象。垃圾收集器通常会在当可用内存减少到少于一个具体的阈值时运行。通常,它们以程序所知的可用的一组“基本”数据 —— 栈数据、全局变量、寄存器 —— 作 为出发点。然后它们尝试去追踪通过这些数据连接到每一块数据。收集器找到的都是有用的数据;它没有找到的就是垃圾,可以被销毁并重新使用这些无用的数据。 为了有效地管理内存,很多类型的垃圾收集器都需要知道数据结构内部指针的规划,所以,为了正确运行垃圾收集器,它们必须是语言本身的一部分。
- 复制(copying): 这些收集器将内存存储器分为两部分,只允许数据驻留在其中一部分上。它们定时地从“基本”的元素开始将数据从一部分复制到另一部分。内存新近被占用的部分现在成为活动的,另一部分上的所有内容都认为是垃圾。另外,当进行这项复制操作时,所有指针都必须被更新为指向每个内存条目的新位置。因此,为使用这种垃圾收集方法,垃圾收集器必须与编程语言集成在一起。
- 标记并清理(Mark and sweep):每一块数据都被加上一个标签。不定期的,所有标签都被设置为 0,收集器从“基本”的元素开始遍历数据。当它遇到内存时,就将标签标记为 1。最后没有被标记为 1 的所有内容都认为是垃圾,以后分配内存时会重新使用它们。
- 增量的(Incremental):增量垃圾收集器不需要遍历全部数据对象。因为在收集期间的突然等待,也因为与访问所有当前数据相关的缓存问题(所有内容都不得不被页入(page-in)),遍历所有内存会引发问题。增量收集器避免了这些问题。
- 保守的(Conservative):保守的垃圾收集器在管理内存时不需要知道与数据结构相关的任何信息。它们只查看所有数据类型,并假定它们 可以全 部都是指针。所以,如果一个字节序列可以是一个指向一块被分配的内存的指针,那么收集器就将其标记为正在被引用。有时没有被引用的内存会被收集,这样会引 发问题,例如,如果一个整数域中包含一个值,该值是已分配内存的地址。不过,这种情况极少发生,而且它只会浪费少量内存。保守的收集器的优势是,它们可以 与任何编程语言相集成。
- 您永远不必担心内存的双重释放或者对象的生命周期。
- 使用某些收集器,您可以使用与常规分配相同的 API。
- 使用大部分收集器时,您都无法干涉何时释放内存。
- 在多数情况下,垃圾收集比其他形式的内存管理更慢。
- 垃圾收集错误引发的缺陷难于调试。
- 如果您忘记将不再使用的指针设置为 null,那么仍然会有内存泄漏。
一 切都需要折衷:性能、易用、易于实现、支持线程的能力等,这里只列出了其中的一些。为了满足项目的要求,有很多内存管理模式可以供您使用。每种模式都有大 量的实现,各有其优缺点。对很多项目来说,使用编程环境默认的技术就足够了,不过,当您的项目有特殊的需要时,了解可用的选择将会有帮助。下表对比了本文 中涉及的内存管理策略。
策略 | 分配速度 | 回收速度 | 局部缓存 | 易用性 | 通用性 | 实时可用 | SMP 线程友好 |
定制分配程序 | 取决于实现 | 取决于实现 | 取决于实现 | 很难 | 无 | 取决于实现 | 取决于实现 |
简单分配程序 | 内存使用少时较快 | 很快 | 差 | 容易 | 高 | 否 | 否 |
GNU malloc | 中 | 快 | 中 | 容易 | 高 | 否 | 中 |
Hoard | 中 | 中 | 中 | 容易 | 高 | 否 | 是 |
引用计数 | N/A | N/A | 非常好 | 中 | 中 | 是(取决于 malloc 实现) | 取决于实现 |
池 | 中 | 非常快 | 极好 | 中 | 中 | 是(取决于 malloc 实现) | 取决于实现 |
垃圾收集 | 中(进行收集时慢) | 中 | 差 | 中 | 中 | 否 | 几乎不 |
增量垃圾收集 | 中 | 中 | 中 | 中 | 中 | 否 | 几乎不 |
增量保守垃圾收集 | 中 | 中 | 中 | 容易 | 高 | 否 | 几乎不 |
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文。
- GNU C Library 手册的 obstacks 部分 提供了 obstacks 编程接口。
- Apache Portable Runtime 文档 描述了它们的池式分配程序的接口。
- Doug Lea 的 Malloc 是最流行的内存分配程序之一。
- BSD Malloc 用于大部分基于 BSD 的系统中。
- ptmalloc 起源于 Doug Lea 的 malloc,用于 GLIBC 之中。
- Hoard 是一个为多线程应用程序优化的 malloc 实现。
- GNU Memory-Mapped Malloc(GDB 的组成部分) 是一个基于 mmap() 的 malloc 实现。
- GNU Obstacks(GNU Libc 的组成部分)是安装最多的池式分配程序,因为在每一个基于 glibc 的系统中都有它。
- Apache 的池式分配程序(Apache Portable Runtime 中) 是应用最为广泛的池式分配程序。
- Squid 有其自己的池式分配程序。
- NetBSD 也有其自己的池式分配程序。
- talloc 是一个池式分配程序,是 Samba 的组成部分。
- Loki C++ Library 有很多为 C++ 实现的通用模式,包括智能指针和一个定制的小对象分配程序。
- Hahns Boehm Conservative Garbage Collector 是最流行的开源垃圾收集器,它可以用于常规的 C/C++ 程序。
- Marshall Kirk McKusick 和 Michael J. Karels 合著的 A New Virtual Memory Implementation for Berkeley UNIX 讨论了 BSD 的 VM 系统。
- Mel Gorman's Linux VM Documentation 讨论了 Linux VM 系统。
关于 malloc 的文章
- Poul-Henning Kamp 撰写的 Malloc in Modern Virtual Memory Environments 讨论的是 malloc 以及它如何与 BSD 虚拟内存交互。
- Berger、McKinley、Blumofe 和 Wilson 合著的 Hoard -- a Scalable Memory Allocator for Multithreaded Environments 讨论了 Hoard 分配程序的实现。
- Marshall Kirk McKusick 和 Michael J. Karels 合著的 Design of a General Purpose Memory Allocator for the 4.3BSD UNIX Kernel 讨论了内核级的分配程序。
- Doug Lea 撰写的 A Memory Allocator 给出了一个关于设计和实现分配程序的概述,其中包括设计选择与折衷。
- Emery D. Berger 撰写的 Memory Management for High-Performance Applications 讨论的是定制内存管理以及它如何影响高性能应用程序。
- Doug Lea 撰写的 Some Storage Management Techniques for Container Classes 描述的是为 C++ 类编写定制分配程序。
- Berger、Zorn 和 McKinley 合著的 Composing High-Performance Memory Allocators 讨论了如何编写定制分配程序来加快具体工作的速度。
- Berger、Zorn 和 McKinley 合著的 Reconsidering Custom Memory Allocation 再次提及了定制分配的主题,看是否真正值得为其费心。
- Paul R. Wilson 撰写的 Uniprocessor Garbage Collection Techniques 给出了垃圾收集的一个基本概述。
- Benjamin Zorn 撰写的 The Measured Cost of Garbage Collection 给出了关于垃圾收集和性能的硬数据(hard data)。
- Hans-Juergen Boehm 撰写的 Memory Allocation Myths and Half-Truths 给出了关于垃圾收集的神话(myths)。
- Hans-Juergen Boehm 撰写的 Space Efficient Conservative Garbage Collection 是一篇描述他的用于 C/C++ 的垃圾收集器的文章。
- 内存管理参考 中有很多关于内存管理参考资料和技术文章的链接。
- 关于内存管理和内存层级的 OOPS Group Papers 是非常好的一组关于此主题的技术文章。
- C++ 中的内存管理讨论的是为 C++ 编写定制的分配程序。
- Programming Alternatives: Memory Management 讨论了程序员进行内存管理时的一些选择。
- 垃圾收集 FAQ 讨论了关于垃圾收集您需要了解的所有内容。
- Richard Jones 的 Garbage Collection Bibliography 有指向任何您想要的关于垃圾收集的文章的链接。
- Michael Daconta 撰写的 C++ Pointers and Dynamic Memory Management 介绍了关于内存管理的很多技术。
- Frantisek Franek 撰写的 Memory as a Programming Concept in C and C++ 讨论了有效使用内存的技术与工具,并给出了在计算机编程中应当引起注意的内存相关错误的角色。
- Richard Jones 和 Rafael Lins 合著的 Garbage Collection: Algorithms for Automatic Dynamic Memory Management 描述了当前使用的最常见的垃圾收集算法。
- 在 Donald Knuth 撰写的 The Art of Computer Programming 第 1 卷 Fundamental Algorithms 的第 2.5 节“Dynamic Storage Allocation”中,描述了实现基本的分配程序的一些技术。
- 在 Donald Knuth 撰写的 The Art of Computer Programming 第 1 卷 Fundamental Algorithms 的第 2.3.5 节“Lists and Garbage Collection”中,讨论了用于列表的垃圾收集算法。
- Andrei Alexandrescu 撰写的 Modern C++ Design 第 4 章“Small Object Allocation”描述了一个比 C++ 标准分配程序效率高得多的一个高速小对象分配程序。
- Andrei Alexandrescu 撰写的 Modern C++ Design 第 7 章“Smart Pointers”描述了在 C++ 中智能指针的实现。
- Jonathan 撰写的 Programming from the Ground Up 第 8 章“Intermediate Memory Topics”中有本文使用的简单分配程序的一个汇编语言版本。
来自 developerWorks
- 自我管理数据缓冲区内存 (developerWorks,2004 年 1 月)略述了一个用于管理内存的自管理的抽象数据缓存器的伪 C (pseudo-C)实现。
- A framework for the user defined malloc replacement feature (developerWorks,2002 年 2 月)展示了如何利用 AIX 中的一个工具,使用自己设计的内存子系统取代原有的内存子系统。
- 掌握 Linux 调试技术 (developerWorks,2002 年 8 月)描述了可以使用调试方法的 4 种不同情形:段错误、内存溢出、内存泄漏和挂起。
- 在 处理 Java 程序中的内存漏洞 (developerWorks,2001 年 2 月)中,了解导致 Java 内存泄漏的原因,以及何时需要考虑它们。
- 在 developerWorks Linux 专区中,可以找到更多为 Linux 开发人员准备的参考资料。
- 从 developerWorks 的 Speed-start your Linux app 专区中,可以下载运行于 Linux 之上的 IBM 中间件产品的免费测试版本,其中包括 WebSphere® Studio Application Developer、WebSphere Application Server、DB2® Universal Database、Tivoli® Access Manager 和 Tivoli Directory Server,查找 how-to 文章和技术支持。
- 通过参与 developerWorks blogs 加入到 developerWorks 社区。
- 可以在 Developer Bookstore Linux 专栏中定购 打折出售的 Linux 书籍。