打印本文 打印本文  关闭窗口 关闭窗口  
涅磐重生C++内存管理变革
作者:佚名  文章来源:不详  点击数  更新时间:2008/4/18 14:38:20  文章录入:杜斌  责任编辑:杜斌

  allocator引起的观念变化

  接触allocator,你可以体会到了它与C++传统的new/delete观念的不同。这主要有以下几点:

  1. 每个类(或者算法)本身,均有最合适它的内存管理机制,并不是向C++传统的做法那样,使用一个全局的new/delete。也许你会说,C++不也允许一个类定义自己的new和delete吗?是的,C++的确支持类定义自己的new/delete,但注意,它的理念和allocator完全不同。我不认为它是C++的一个优秀之作,相反,它起到了误导作用。

  因为,决定一个类对象怎么去new出来,并不是取决于该类本身,而相反是取决于使用该类的人。一个类不需要关心自身被如何创造出来,更不能假定。它需要关心的是它自己的类成员如何被创建出来,它的算法(你可以把类看做一个算法集合)涉及到的所有组件如何被创建出来。而这,才是allocator带来的观念。

  让各种各样的allocator创建同一个类的不同实例,这些实例甚至可能在一起工作,相互协作。从STL的角度讲,这完全是最正常不过的事情了。

  2. 重要的是由allocator创建管理对象,避免在你的代码中使用new/delete。如果可能,你可以如STL那样,将allocator作为模板参数,不绑定具体的某个内存管理器。但是,如果你的算法依赖了某个allocator的实现特有的功能,这也并不要紧。你的目的不是要做到allocator的可替换,不是吗?重要的是使用了这个allocator了,它给你在内存管理上带来了益处。

  但是,应该看到,STL实作的各种allocator,目前来看除了最简单使用malloc/free实现的外,主要就是基于mempool技术。而该技术的目标,不是让内存使用者更加方便有效地进行内存管理,而更多的是关注于内存分配的时间性能。为了让C++程序员从内存管理中解脱出来,我们需要实作新的alloctor,需要新的突破!

  新视角:具垃圾回收能力的Allocator

  对,我设想的一个做法是,贯彻STL的allocator观念,并且提供具备特定的内存管理能力(例如垃圾回收)的各种allocator。让C++社区广泛接受allocator观念,并且从中受益。C++程序员是时候抛弃传统的new/delete,让他们退出历史舞台了。

  我接下来会实作两个具体的allocator(均属原创)。相信它们会让你耳目一新,让你不禁想到:哦,原来在C++中,我还可以这样进行内存管理。

  当然,我最大的希望就是,这两个allocator能够起到抛砖引玉的作用,让大家也清楚地意识到allocator的重要性,可以出现更多的具备各种能力的allocator,解脱C++程序员一直以来的苦难(可能是最大苦难)。

  这两个allocator均具备一定程度的垃圾回收能力。只是观念上各有各的侧重。我们接下来会分为两个专题专门对它们进行阐述。

  辅助的New过程

  我们终于可以开始讨论前文提到的New函数的实现上了。以不带参数的New为例,它的代码如下,可能并没有你想象的那么复杂:

#include
template
inline Type* New(AllocType& alloc)
{
 void* obj = alloc.Alloc(sizeof(Type), DestructorTraits::Destruct);
 return new(obj) Type;
}

  其中DestructorTraits是一个根据类型Type萃取[4]析构函数的萃取器。它看起来是这样的:

template
struct DestructorTraits
{
 static void Destruct(void* pThis)
 {
  ((Type*)pThis)->~Type();
 }
};

  这样,你就可以通过以下代码new出对象了:

MyClassA* obj = New(alloc);
MyClassB* obj = New(alloc);

  特别提醒:这里New函数在VC++ 6.0下编译通过,但是产生的执行代码存在严重bug。如果你只New一类对象,没有问题,但在New了多种对象后,似乎VC++对MyClassA、MyClassB 两者混淆起来了。为了支持VC++ 6.0,你需要对这里的New做出调整。

  COM技术与内存管理

  已经准备结束这篇短文的时候,忽然想到了长久以来使用COM技术形成的一些感想,这些想法恰恰与内存管理紧密相关。故此想就这个问题陈述一下。

  从COM的IUnknown接口看,它主要关注两个问题:一个是QueryInterface,一个是引用计数(AddRef/Release)。COM组件很讲究信息的屏蔽,使用者对组件的认识有限,这就给组件升级、扩充功能提供了可能。QueryInterface是一个很好的概念,需要发扬光大。

  COM的引用计数则关注的是组件的生命期维护问题。换句话说,就是组件如何销毁的问题。诚然,组件对象的销毁问题,是内存管理的关键。无论是COM的引用计数,还是垃圾回收技术,均是要解决对象的销毁问题。只是两者的侧重点不太一样,COM引用计数更关注“确保组件不会被提前销毁了,确保组件访问的安全性”,而垃圾回收器则关注“不管怎样确保组件最终被销毁,没有内存泄漏”。

  在COM中,确保组件访问的安全性(避免非法访问),这个观点太重要了,以至于它甚至不惜加重程序员的内存管理负担。所以,在COM程序中,出现内存泄漏太正常了,而且一旦泄漏通常就是大片大片内存的漏。更加要命的是,你甚至不能有一个很简单有效的方法确认这个泄漏是由于哪段代码引起。因为组件所有的客户都是平等的,任何一个客户代码存在问题均将导致内存的泄漏。

  刚开始接触COM技术的时候,我对引用计数持的是比较正面的态度。但是随着部门逐步加大COM技术的使用力度后,四五年下来,我渐渐开始迷惑起来。一切并不如想象的那样。这个引用计数的背后,需要我们付出多少额外的代价!

  而这个迷惑、思索,可能就是本文以及后续相关内容的成因吧。
打印本文 打印本文  关闭窗口 关闭窗口