共享内存上的c++内存池实现:这种分配和释放的方法是否正确?
C++ memory pool on shared memory implementation: Is this method of allocation and deallocation correct?
我可以要求我的任何回应者只考虑"纯"C/c++(无论这意味着什么)吗?STL没问题。
我正在编写自己的c++内存池类(在Linux系统上),用于在共享内存中分配和释放c++对象。我需要通过多个进程访问相同的对象。我将使用POSIX信号量控制对内存池对象操作的访问,但是我有一个基本的分配/释放问题。我的代码只适用于从同一池中分配的相同大小的对象。目前,我们可以忽略与池的动态增长和收缩相关的问题。
考虑我有一个共享内存段定义了MAXFOOOBJECTS Foo对象的总数。我以以下方式定义共享内存段:
int shmid = shmget (somekey, ((sizeof(Foo) + 4) * MAXFOOOBJECTS) + 4, correctFlags);
void* sMem = shmat (shmid, (void*)0, 0);
对于所有使用这个共享内存的进程,内存将被这样解释:
struct SharedMemStructure
{
int numberOfFooObjectsInPool;
Foo* ptrArray [MAXFOOOBJECTS]; // Pointers to all the objects in the array below
Foo objects [MAXFOOOBJECTS]; // Same as the value in the shmget call
};
假设我有一个对象Foo,定义如下:
<Foo.h>
class Foo
{
public:
Foo ();
~Foo ();
void* operator new (); // Will allocate from shared memory
void operator delete (void* ptr); // Will deallocate from shared memory
private:
static void* sharedMem; // Set this up to be a POSIX shared memory that accesses
// the shared region in memory
static int shmid;
}
<Foo.cpp>
int Foo::shmid = shmget (somekey, ((sizeof(Foo) + 4) * MAXFOOOBJECTS) + 4, correctFlags);
void* Foo::sharedMem = shmat (shmid, (void*)0, 0);
void* Foo::operator new ()
{
void* thisObject = NULL;
sem_wait (sem); // Implementation of these is not shown
// Pick up the start of a chunk from sharedMem (will make sure this
// chunk has unused memory...
thisObject = (sharedMem + 4 + 4 * MAXFOOOBJECTS +
(sizeof (Foo) * sharedMem->numberOfFooObjectsInPool);
sharedMem->ptrArray[numberOfFooObjectsInPool] = thisObject;
sharedMem->numberOfFooObjectsInPool ++;
sem_post (sem);
return thisObject;
}
void Foo::operator delete (void* ptr)
{
int index = 0;
sem_wait (sem); // Implementation of these is not shown
// Swap the deleted item and the last item in the ptrArray;
index = (ptr - (sharedMem + 4 + (4*MAXFOOOBJECTS)))/(sizeof(Foo));
ptrArray[index] == ptrArray[numberOfFooObjectsInPool - 1];
numberOfFooObjectsInPool --;
sem_post (sem);
}
我的问题是:
- 前面提到的方案对你们来说还行吗(每个新建和删除都是0(1)),还是我遗漏了一些非常重要的东西?我立即看到的一个问题是,如果Foo对象数组被解释为最小堆,例如,每次执行new和delete操作时,我都会杀死堆属性。
- 如果我保证这个池不会被用于最小堆(比如,定时器管理技术需要),我们对上述方案有任何问题吗?
- 另一方面,我可以将共享内存中的Foo数组管理为最小或最大堆(即,在新建和删除期间),并且每次新建或删除都会导致O (lgn)最坏情况。任何评论?
- 还有其他更好的方法吗?
我觉得你的想法不错,但是你的指针算术有点麻烦…而且也不能携带。一般来说,你永远不应该访问一个结构体的成员,添加前一个成员的大小,因为这是完全不可移植的(而且相当难看)。请记住,编译器可能对结构体的成员有对齐限制,因此它可能在它认为合适的地方插入填充字节。
使用您展示的struct SharedMemStructure
更容易:
int shmid = shmget (somekey, sizeof(SharedMemStructure), correctFlags);
SharedMemStructure* sharedMem = static_cast<SharedMemStructure*>(shmat (shmid, (void*)0, 0));
则在operator new
中:
//...
thisObject = &sharedMem[sharedMem->numberOfFooObjectsInPool];
//...
关于你的问题:
- 当然,恒定的分配复杂度与堆属性不兼容。我认为O(log n)是你能得到的最好结果。
- 我看到的方案很好,但细节是在这些Foo类包含什么。只要它没有虚函数、虚基类、指针、引用或任何其他c++结构,就可以了。 是的,你可以,为什么不呢?
- 如果你不需要堆属性,一个通常和简单的优化是摆脱
ptrArray
成员,并使用每个空闲槽的第一个字节来构建一个空闲槽列表,以指向下一个空闲槽。
将所有的4替换为sizeof(int)和sizeof(Foo *),以提高可移植性和可读性。或者更好的是,实际使用你已经定义的SharedMemStructure。
修改SharedMemStructure,然后开始使用它。你用来追踪哪个槽被使用的算法有缺陷。一旦一个项被删除,指针列表也被调整,如果不遍历整个列表,就无法知道哪些槽被使用了。一个简单的bool数组也可以,但它仍然需要遍历列表。如果你真的关心O(n),你需要维护使用的和空闲的链表。这可以用一个固定大小的int数组来完成。
size_t indices[MAXFOOOBJECTS];
size_t firstUsedIndex;
size_t firstFreeIndex;
初始化firstUsedIndex为MAXFOOOBJECTS, firstFreeIndex为0。通过MAXFOOOBJECTS将indexes初始化为1。通过这种方式,您可以将索引中的每个条目视为链表,其中内容是"下一个"值,MAXFOOOBJECTS是列表结束符。分配可以在固定时间内完成,因为您可以获取列表的前节点。释放将是线性的,因为您必须在已用列表中找到节点。你可以使用(ptr - poolStart)/sizeof(Foo)快速找到节点,但你仍然需要找到前一个节点。
如果你也想消除重新分配的成本,将索引的大小加倍,并将其视为一个双重链表。代码是类似的,但现在您可以在常量时间内完成所有操作。
这看起来是个问题:
int main() {
Foo* a = new Foo; // a == &sharedMem->objects[0]
Foo* b = new Foo; // b == &sharedMem->objects[1]
// sharedMem->ptrArray: {a, b, ...}
delete a;
// sharedMem->ptrArray: {b, ...}
Foo* c = new Foo; // c == &sharedMem->objects[1] == b!
}
- 包含矢量指针的结构的内存释放问题
- Valgrind 声称内存释放中的自由空间太多
- 当 C 和 C++ 中严格要求内存释放时
- C++ 中指针变量的内存释放
- 德克内存释放
- 临时对象 c++ 的内存释放
- C 全局对象变量内存释放
- C++矢量动态内存释放/删除
- 从 cv::Mat 初始化的 IplImage 的内存释放
- SWIG类型映射中的内存释放
- STL容器中的内存释放
- QWebView内存释放
- 内存释放 C++
- 大返回值(如字符串)的内存释放如何在C++中发生
- 重新分配之前的内存释放
- Linux C++ 中的内存释放
- VS2012编译器奇怪的内存释放问题
- 调试断言失败!错误的内存释放
- 负责 COM 互操作中的内存释放
- imread命令后OpenCV矩阵内存释放