内存领域的异常安全
我正在编写一个简单的内存分配器,并面临异常安全的小问题。这种情况是当您分配一个对象时,该对象本身会调用分配器。内存池的目标是一次分配一堆对象,然后在池销毁时将它们全部删除。
{
MemoryArena m;
std::string* ptr = m.Allocate<std::string>();
// use ptr whatever
// Cleaned up when pool is destroyed
}
但当多次使用时,这会变得相当令人讨厌。如果内部分配被清理,那么它可以在以后使用——这不是一个坏的假设,因为池的定义是在它的生命周期结束之前永远不会删除对象。考虑:
struct X {
X(MemoryArena* ptr, std::string*& ref) {
ref = ptr->Allocate<std::string>();
throw std::runtime_error("hai");
}
};
MemoryArena m;
std::string* ptr;
m.Allocate<X>(&m, ptr);
// ptr is invalid- even though it came from the arena
// which hasn't yet been destroyed
但是如果内部分配没有被清理,外部分配也无法被清理,因为内存区域像在硬件堆栈上一样线性分配它们,所以我泄漏了内存。因此,要么我通过提前销毁对象来违反我的语义,要么我泄漏内存。
对于如何解决这个问题有什么建议吗?
I'm writing a simple memory arena allocator and facing a small problem with exception safety. The situation is when you allocate an object which itself calls the allocator. The objective of the memory pool is to allocate a bunch of objects at one time, and then delete them all when the pool is destroyed.
{
MemoryArena m;
std::string* ptr = m.Allocate<std::string>();
// use ptr whatever
// Cleaned up when pool is destroyed
}
But this gets rather nasty when it's used multiple times. If the inner allocation is cleaned up, then it could be used afterwards- not a bad assumption, since it's the definition of the pool to never delete objects until it's lifetime is over. consider:
struct X {
X(MemoryArena* ptr, std::string*& ref) {
ref = ptr->Allocate<std::string>();
throw std::runtime_error("hai");
}
};
MemoryArena m;
std::string* ptr;
m.Allocate<X>(&m, ptr);
// ptr is invalid- even though it came from the arena
// which hasn't yet been destroyed
But if the inner allocation isn't cleaned up, the outer allocation also can't be cleaned up, because the memory arena allocates them linearly like on a hardware stack, so I leak memory. So either I violate my semantics by destroying an object early, or I leak memory.
Any suggestions for how to resolve this problem?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
也许这只是适用的示例代码,但我认为用户不应该假设当
X
的构造函数抛出时ptr
是有效的。它也可能在 ref 被分配之前抛出。所以我想说,如果可以的话,清理内部对象(即,如果竞技场的前面当前位于内部对象的末尾)。好吧,来自竞技场的分配变得无效,这是不正常的,但无论如何,这是一个永远不应该进入现实世界的分配。
也许您可以使用“软”分配的概念来明确这一点。它不能保证永远存在,因为虽然仍然“软”,但它可以被释放回竞技场。那么 X 的构造函数会执行类似的操作:
在没有首先调用
release
的情况下执行 SoftPtr 的析构函数意味着没有公开对该对象的引用。它调用 MemoryArena 的函数,该函数执行以下操作:因此,只要以相反的顺序完成,就可以“撤消”任意数量的分配。
Maybe it's just the example code that this applies to, but I don't think the user should assume that
ptr
is valid when the constructor ofX
throws. It could just as well have thrown before ref was assigned.So I'd say clean up the inner object if you can (i.e. if the front of the arena currently lies at the end of the inner object). OK, an allocation from the arena becomes invalid, which isn't normal, but it's an allocation that should never had made it out into the real world anyway.
Perhaps you could make this explicit, with a concept of a "soft" allocation. It's not guaranteed to live forever, because while still "soft" it can be freed back to the arena. Then X's constructor would do something like:
Executing the destructor of
SoftPtr
without without first callingrelease
implies that no reference to the object has been exposed. It calls a function of MemoryArena that does something like:So any number of allocs can be "backed out", provided it's done in reverse order.
根据内存池的语义,正如您在问题中所说,只有整个池才能被释放,而不能释放单个对象。但你确实想这么做。
一种可能是为分配器配备类似brk的函数来获取和设置下一个分配地址。这为您提供了一个低级机制,您可以使用它来构建您想要的任何内容。
By the very semantics of memory pools, and as you have stated yourself in the question, only the pool as a whole can be freed, not individual objects. Yet you want to do exactly that.
A possibility is to equip the allocator with brk-like functions to get and set the next allocation address. This gives you a low-level mechanism that you can use to built whatever you want on top of it.