这个问题需要一些解释,如果你不跳过这个例子,请竖起大拇指:)
我最近在一些细节上读了一本描述内存碎片(在堆上)的书,它让我想到了我自己的项目。例如,以下列方式使用ptr_container(来自Boost)
ptr_array<A> arr; //
arr.push_back(new A()); // a1.
arr.push_back(new A()); // a2.
arr.push_back(new A()); // a3.
....
在更换元素时会很快导致一些内存碎片。为了论证,我们可以说实际的容器可以容纳我们给它的所有指针。堆看起来像:
[arr_array][a1][a2][a3]...[aN]
当我们开始用子类型(具有更大的大小)替换指针时,这种情况会发生变化,假设我们将奇数指针(a1,a3,...)引用的所有对象替换为更大的类型,然后它'我看起来像:
[arr_array][xx][a2][xx][a4]...[aN][b1][b3]...[bN]
其中[xx]表示未使用的空间,b1 ... bN是新对象。
所以我想要的是一个存储对象的容器(比如在STL容器中),但支持多态存储。我知道如何实现这种容器,但我更喜欢使用“专家”库(Boost,STL,...)。总结一下,我的问题是:
是否有一个容器支持(动态分配)保存在连续内存序列中的多态对象?
例如,内存布局可能如下所示:
[arr_array] = [ptr_lookup_table][storage]
= [p1, p2, ..., pn][b1, a2, b3, ..., aN]
感谢您的回答/评论!
答案 0 :(得分:2)
内存碎片需要预知内存分配,所以我需要先设置一些概念。
内存分配
当您调用operator new
(默认情况下通常会在后台调用malloc
)时,您不会直接从操作系统请求内存,而是(通常)会发生以下情况:
malloc
76个字节,它看起来是否可用:
内存碎片可能发生在两个层面:
通常,由于malloc
应该一次调用4KB的页面(除非你要求更大的块,在这种情况下它将选择4KB的更大倍数),你永远不应该耗尽你的地址空间。它发生在32位机器上(限制为4GB),但是它的分配异常大。
另一方面,如果malloc
的实现过于天真,那么最终可能会出现内存块碎片,因此内存占用的内存比实际使用的内存大得多。这通常是内存碎片这个术语现在所指的。
典型策略
当我说天真时,我指的是你想要不断分配所有东西的想法。这是一个坏主意。这通常不会发生什么。
相反,今天好的malloc
实现使用了池。
通常,他们将拥有每个尺寸的:
当你提出请求时,他们会找到满足它的最小尺寸的游泳池,这个游泳池将为你服务。
因为在池中所有请求都以相同的大小提供,所以池中没有碎片,因为 free 单元可用于任何传入请求。
那么,碎片?
如今,你不应该观察到碎片本身。
但是你仍然可以观察到记忆孔。假设一个池正在处理9到16个字节的对象,你可以分配4,000,000个对象。这需要至少16,000页的4KB。现在假设您释放了除了16,000个之外的所有内容,但是狡猾地使每个页面仍然存在一个对象。操作系统无法回收页面,因为您仍然使用它们,但是由于您只使用了4KB中的16个字节,因此空间非常浪费(目前)。
使用垃圾收集的某些语言可以使用压缩来处理这些情况,但是在C ++中,由于用户可以直接控制对象地址,因此无法在内存中重定位对象。
魔术容器
我不知道有这样的野兽。我不明白为什么它也会有用。
<强> TL; DR 强>
不要担心碎片。
注意:“专家”可能想写自己的池分配机制,我想提醒他们不要忘记对齐
答案 1 :(得分:0)
(编辑:抱歉,误读了您的问题;之前的答案已删除。)
您可以为对象使用任何内存池。通常,您将同一池中的相同(或类似)大小的对象组合在一起。由于您通常需要在池中调用特殊的delete
函数,因此我建议您使用带有自定义删除器的shared_ptr
。然后,您可以将此shared_ptr
与您喜欢的任何标准容器一起使用。
编辑:似乎需要一个例子。警告:这是完全未经测试的,从我的头顶。不要指望它编译。</ p>
#include <boost/pool.hpp>
#include <memory>
class A;
class B; // B inherits from A
int main() {
// could be global
boost::object_pool<A> a_pool;
boost::object_pool<B> b_pool;
std::vector<std::shared_ptr<A>> v;
v.push_back(std::shared_ptr<A>(a_pool.construct(), [&](A*p) { a_pool.free(p); }));
v.push_back(std::shared_ptr<A>(a_pool.construct(), [&](A*p) { a_pool.free(p); }));
v.push_back(std::shared_ptr<A>(a_pool.construct(), [&](A*p) { a_pool.free(p); }));
v[2] = std::shared_ptr<B>(b_pool.construct(), [&](B*p) { b_pool.free(p); });
return 0;
}
即使B比A大得多也行。它也不依赖于自动释放池是恕我直言的危险。内存布局不紧,因为池总是会分配,但它没有碎片,如果我理解你的问题,这就是你想要的。
答案 2 :(得分:0)
由于使用了boost容器,因此碎片发生而不是。当您经常使用new
和delete
分配和取消分配不同大小的对象时,就会发生这种情况。 ptr_array
只是存储指向这些已分配对象的指针,并且可能不会显着地导致碎片化。
如果要计算内存碎片,可以重载对象operator new
并实现自己的内存管理系统。我建议你阅读memory pools和free lists的主题。