ACE
构架含有一组非常丰富的内存管理类。这些类使得你能够很容易和有效地管理动态内存(从堆中申请的内存)和共享内存(在进程间共享的内存)。你可以使用若干不同的方案来管理内存。你需要决定何种方案最适合你正在开发的应用,然后采用恰当的ACE类来实现此方案。ACE
含有两组不同的类用于内存管理。第一组是那些基于
ACE_Allocator的类。这组类使用动态绑定和策略模式来提供灵活性和可扩展性。它们只能用于局部的动态内存分配。第二组类基于
ACE_Malloc模板类。这组类使用C++模板和外部多态性(External Polymorphism)来为内存分配机制提供灵活性。在这组类中的类不仅包括了用于局部动态内存管理的类,也包括了管理进程间共享内存的类。这些共享内存类使用底层OS(OS)共享内存接口。为什么使用一组类而不是另外一组呢?这是由在性能和灵活性之间所作的权衡决定的。因为实际的分配器对象可以在运行时改变,
ACE_Allocator类更为灵活。这是通过动态绑定(这在C++里需要使用虚函数)来完成的,因此,这样的灵活性并非不需要代价。虚函数调用带来的间接性使得这一方案成了更为昂贵的选择。另一方面,
ACE_Malloc类有着更好的性能。在编译时,malloc类通过它将要使用的内存分配器进行配置。这样的编译时配置被称为“外部多态性”。基于ACE_Malloc的分配器不能在运行时进行配置。尽管ACE_Malloc效率更高,它不像ACE_Allocator那样灵活。分配器(Allocator)分配器用于在
ACE中提供一种动态内存管理机制。在ACE中有若干使用不同策略的分配器可用。这些不同策略提供相同的功能,但是具有不同的特性。例如,在实时系统中,应用可能必须从OS那里预先分配所有它将要用到的动态内存,然后在内部对分配和释放进行控制。这样,分配和释放例程的性能就是高度可预测的。所有的分配器都支持
ACE_Allocator接口,因此无论是在运行时还是在编译时,它们都可以很容易地相互替换。这也正是灵活性之所在。所以,ACE_Allocator可以与策略模式协同使用,以提供非常灵活的内存管理。表3-1给出了对ACE中可用的各种分配器的简要描述。这些描述规定了每种分配器所用的内存分配策略。
分配器 | 描述 |
ACE_Allocator | ACE 中的分配器类的接口类。这些类使用继承和动态绑定来提供灵活性。 |
ACE_Static_Allocator | 该分配器管理固定大小的内存。每当收到分配内存的请求时,它就移动内部指针、以返回内存 chunk(“大块”)。它还假定内存一旦被分配,就再也不会被释放。 |
ACE_Cached_Allocator | 该分配器预先分配内存池,其中含有特定数目和大小的内存 chunk。这些chunk在内部空闲表(free list)中进行维护,并在收到内存请求(malloc())时被返回。当应用调用free()时,chunk被归还到内部空闲表、而不是OS中。 |
ACE_New_Allocator | 为 C++ new和delete操作符提供包装的分配器,也就是,它在内部使用new和delete操作符,以满足动态内存请求。 |
表
3-1 ACE中的分配器3.1.1使用缓存式分配器(Cached Allocator)ACE_Cached_Allocator
预先分配内存,然后使用它自己内部的机制来管理此内存。这样的预分配发生在类的构造器中。所以,如果你使用此分配器,你的内存管理方案仅仅在开始时使用OS分配接口来完成预分配。在那以后,ACE_Cached_Allocator将照管所有的内存分配和释放。为什么要这样做呢?答案是性能和可预测性。设想一个必须高度可预测的实时系统:通过
OS来分配内存将涉及昂贵和不可预测的OS内核调用;相反,ACE_Cached_Allocator不会涉及这样的调用。每一次分配和释放都将花费固定数量的时间。

图
3-1缓存式分配器
图
3-1演示缓存式分配器。在构造器中预分配的内存在空闲表中进行内部管理。该表将若干内存chunk作为它的节点。这些chunk可以是任何复杂的数据类型,你可以按你所希望的那样规定它们的实际类型。后面的例子会说明怎样去做。在此系统中分配和释放涉及固定数量的空闲表指针操作。当用户请求内存
chunk时,他将获得一个指针,而空闲表被调整。当用户释放内存时,它将回到空闲表中。如此循环往复,直到ACE_Cached_Allocator被销毁,所有的内存随之被归还给OS。在内存被用于实时系统时,需要考虑chunk的内部碎片。下面的例子演示
ACE_Cached_Allocator是怎样被用于预分配内存,然后处理内存请求的。
