池内春秋

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

/programmer-13-memory-pool.htm
2009-5-6

传送日期: 2002年9月25日 PM 03:22 您好:我正在拜读您的《STL源码剖析》,近日遇到一个问题,希望能向您请教。 关於__default_alloc_template,我写了一些代码测试,发现对於大於128bytes的内存分配没有问题,但是当我分配一些小於128bytes的内 存时: 确实分配了内存池,并用free list把内存串起来,整个分配过程都很正常。 但我认为释放时存在问题,释放内存时调用以下函数: static void* allocate(size_t __n) { ........ else { _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n); ........ _Obj* __RESTRICT __result = *__my_free_list; if (__result == 0) __ret = _S_refill(_S_round_up(__n)); else { *__my_free_list = __result -> _M_free_list_link; __ret = __result; } } } 只是简单的调整指针,并没有真正的释放内存,这也可以理解,因为只是释放一个块,不应该释放整个内存池。那麽整个内存池在什麽地方 释放呢? 我发现__default_alloc_template类并没有析构函数(deconstructor),内存池没有被释放,我跟踪了内存分配和释放函数,确实发生了 内存泄漏。 我的分析对吗?这是SGI的bug吗? 谢谢指教 ●侯捷回覆:这不是 bug,这是设计理念。 memory pool 一般并不释放 blocks。因为它认为资源由它管理。这不算是 memory leak。 但这的确有缺点。应该适时释放(一些)blocks。这是 SGI allocator 值得加强的部分,也是我在《池内春秋》一文中忘了强调的。该文 发表於《程序员》九月、十月两期。目前下期还未刊出,附上全文 PDF,博君一哂。 至於程式结束前一刻没有释放整个 pool,那没有关系,modern OS 自会收回这一部份,不影响其他 process。
池内春秋
Page 1 of 2
池内春秋
Memory Pool 的设计哲 设计哲学与无痛运用
文章: 文章:programmerprogrammer-1313-memorymemory-pool.pdf
寄件者: "Peng Chunhua" <chpeng@> 传送日期: 2002年9月12日 AM 11:00 主旨: 关於 [池内春秋] 文章的一个问题 To:侯先生 您好,一直对先生的文章很感兴趣,经常于谈笑中分析问题,让人茅塞顿开,获益非浅。谢谢。 先生在最近一期的《程序员》杂志(2002年9期)上发表了一篇文章[池内春秋]介绍了Memory Pool的技术。不过,不知道什麽原因,在《程 序员》上一定要分上下篇发表。所以目前还不太清楚Memory Pool的实质内容。不过,在上篇中关於[空间上的额外开销]和[速度上的额外开 销]的说法,本人有点疑问,望先生指教。 一,空间上的额外开销 先生认为在C++平台提供的内存配置工具中会带来额外的配置,即Cookie。并进行了明证。 其实我认为这个证明是有误的,首先VC6和C++Builder在内存管理上就是不一样的机制。通过分析,我发现new在底层肯定是调用malloc的 (在C++平台上)。所以,只要分析一下malloc的实现机制就可以了。在VC6.0上,如果用Debug版的话,就和先生说的一样,有额外开销, 基本上是32个字节,用来记录内存链表和分配内存的源文件名,行号,字节数,第几次分配的一些信息。这也就是VC6可以在Debug版可以检 查内存泄漏的原因。具体叁考一下malloc的Debug版实现的代码就可以分析出来的。在VC6.0的Release版上,就没有这些额外记录了, malloc的实现是直接调用HeapAlloc的,释放也是直接调用HeapFree的,VC本身并没有对内存进行任何多馀的操作。所以在Release版中 malloc的返回值等於HeapAlloc的值,而Debug中malloc的值就等於HeapAlloc() + 0x20 的值。至於内存的大小,在Release版中也是直接通 过HeapSize得到的。并且在VC中同一个模块中所有的malloc, new都是在一个Heap上操作的,在VC的运行代码中是_crtheap。这也就是为什 麽在申请10000000个C1的对象时VC比C++Builder中花费的时间比较长的一个原因。不过,在Release版中,申请内存的头部确实有32个字节 记录了内存的一些信息,比如大小。但该Cookie和C++平台无关,也就是说,所有用HeapAlloc分配的内存在头部都有32个字节的额外开销。 (好像不止32个字节,并且在申请内存的末尾也有一些标记) 在C++Builder中管理内存和VC中是不一样的。C++Buider中不用Heap进行内存的分配,而是自身通过TMemoryManager进行内存管理。具体方 式是通过VirtualAlloc一次分配16KByte字节,当程序通过malloc分配内存时,C++Builder就遍历自身管理的内存,将空闲的内存进行分 配,并在头部记录内存的大小。当内存不足时,再一次调用VirtualAlloc向系统申请内存,由C++Builder进行分配管理。所以,先生在空间 之明证上说明C++Builder表现很好。 二,速度之明证 前面比较了VC6.0和C++Builder的内存管理上的不同,那麽,对於在速度上VC和C++Builder的差异也就不难理解了。VC中的内存分配在一个 模块中都是在_crtheap上分配的,当再次申请内存时,VC中必须由系统Heap遍历整个Heap区进行申请。而C++Builder只需首先遍历自身管理 的VirtualAlloc链表,发现VirtualAlloc中有空闲内存时再遍历该VirtualAlloc的内存区域。这样由於一个VirtualAlloc对应了很多malloc 的内存,在遍历整个内存的时间上就比VC快了。比如:一次VirtualAlloc的内存可以管理m个malloc,那麽对已经分配了n*m个内存而言,再 分配一个内存的花费为: VC = n*m C++Builder=n+m 同样可以推断:如果C++Builder在管理内存上一次VirtualAlloc的大小不是16K(0x4000)而是更大,在这种分配1000000个内存时速度应该更 快一点。(不过,效果估计不明显)根据上面对C++Builder的内存管理分析,也就可以理解为什麽在C++Builder的程序中会出现经过一系列内 存申请和释放後,通过TaskManager观察内存使用量并不一定回到执行这些操作之前了。因为VirtualAlloc中的某一块内存被使用的话,即 使其他内存都被释放了,C++Builder也不能将该内存提交给系统释放。 对於GCC的编译器我没有研究过,在此不敢发表看法。 E-mail:chpeng@ ●侯捷回覆 感谢您的意见和补充。很有价值,请允许我日後将您的来信放在侯捷网站上做为 "池内春秋" 一文的补充。 我尚未能够仔细思考你所提的技术深处。不同的编译器的确在表层之下又做了许多功夫,你对VC和BCB的理解非常到位。我认为,VC 底层呼 叫 HeapAlloc(),而後者一样需要额外开销(否则根本无法管理blocks,除非HeapAlloc()已经使用了 memory pool)。Windows30/31/95 时代 我对其上的记忆体配置策略也多有研究,後来不再走 platform-specific 主题,就放下了。 从您的信上看来,您深入追踪了 VC malloc debug/release 版的实作码,这使我很尊敬您的专业。我一直打算深刻追踪 Doug Lea 的 malloc() 演算法,不过还没能拨出时间。 杂志有杂志的考量,因此常将我的长文章分为下上。我虽不喜欢看到这样的结果(我想读者也都不喜欢),但能够体谅杂志社的难处。随信 附上 "池内春秋" 全文。该文将於刊毕後开放於侯捷网站。 传送日期: 2002年9月16日 PM 02:08 谢谢先生的回信。看了Memory Pool的全文,对Memory Pool有了一个新的了解。 正如先生所讲,在HeapAlloc的头部确实有额外的开销维护内存的大小,并且在末尾也有一点
/programmer-13-memory-pool.htm
2009-5-6
池内春秋
Page 2 of 2
标记。在未看到全文时对Memory Pool没有深入了解,不是很明白Memory Pool的功能,让先生 见笑了。 看过Memoey Pool全文後,有几点认识: 1,Memory Pool从来都不向系统提交释放内存的请求。所以内存只会增加不会减少。 2,Memory pool的内存大小为某一时刻通过Memory Pool提交内存的最大值。 3,Memory pool在频繁使用new,delete时对内存的申请上不会有太大的问题,但当一开始集中使用new再後来集中使用delete进行释放时将 导致内存浪费(因为Memory pool根本不向系统释放内存,幸好这种用法不多) 4,如果VC6.0中使用Memory pool,进行Debug时,我认为VC6.0可能会通知有内存泄漏。 5,同样,BoundsChecker也可能会通知有内存泄漏(Memory Leak)。作为编程人员,可能产生迷惑,不知先生对此有何看法。 另外,在先生的效率加快证明上,有一个疑问点,即:VC6中allocator比new慢。经过份析,发现先生的一个小Bug,即申请内存的大小不 同,所以就没有可比性。见先生的注释 9。 MyAlloc.allocate(size, (int*)0);实际上申请的是size*sizeof(int*)=64字节。 而通过new C1的申请的内存大小是16字节。那麽,在大量申请内存时产生的临时交换文件的时间可能就不可无视之了。不过CB5通过new分配 内存只需要6S就有点不可理解了。在第一回测试中花费29S,後来只花费6S确实令人费解。不知先生的测试环境如何:CPU,OS,Memory等情 况。 真诚希望今後能得到先生的指导,聆听先生的教诲。 ●侯捷回覆:
相关文档
最新文档