我遇到了一个奇怪的行为。在我的代码中,一个变量递减,但不递增,因此我的算法不起作用。变量名称为blocksAvailable
,它在Chunk
类中定义,使用Chunk::init
方法启动,使用Chunk::allocate
方法递减,并且必须使用Chunk::deallocate
方法递增。因此,只有两个地方提到了这个变量 - allocate
和deallocate
方法。在一个地方它会减少(并且它可以工作)而在其他地方它会增加并且它不起作用。这是完全最小化和可重现的代码:
#include <cstddef>
#include <iostream>
#include <vector>
using uchar = unsigned char;
class Chunk
{
private:
friend class FixedAllocator;
void init(size_t blockSize, uchar blocks);
void release();
void* allocate(size_t blockSize);
void deallocate(void* p, size_t blockSize);
inline bool hasBlock(void* p, size_t chunkLen) const
{
uchar * pc = static_cast<uchar*>(p);
return (pData <= pc) && (pc <= (pData + chunkLen));
}
inline bool releasable(uchar numBlocks) const
{
return blocksAvailable == numBlocks;
}
uchar* pData;
uchar firstAvailableBlock, blocksAvailable;
};
void Chunk::init(size_t blockSize, uchar blocks)
{
// for n of Ts it will allocate n * sizeof(T) memory
pData = new uchar[blockSize * blocks];
firstAvailableBlock = 0;
blocksAvailable = blocks;
uchar i = 0;
uchar* p = pData;
// used by allocate method to move forward firstAvailableBlock
for (; i != blocks; p += blockSize)
{
*p = ++i;
}
}
void Chunk::release()
{
::operator delete(pData);
}
void* Chunk::allocate(size_t blockSize)
{
if (!blocksAvailable) return 0;
// move firstAvailableBlock one block ahead
uchar* pResult = pData + firstAvailableBlock * blockSize;
firstAvailableBlock = *pResult;
--blocksAvailable;
std::cout << "blocksAvailable after allocate " << blocksAvailable << std::endl;
return pResult;
}
void Chunk::deallocate(void* p, size_t blockSize)
{
uchar* toRelease = static_cast<uchar*>(p);
// find last but one available block
firstAvailableBlock = static_cast<uchar>((toRelease - pData) / blockSize);
++blocksAvailable;
std::cout << "blocksAvailable after deallocate " << blocksAvailable << std::endl;
}
class FixedAllocator
{
private:
size_t blockSize;
uchar blocks;
using Chunks = std::vector<Chunk>;
Chunks chunks;
Chunk* allocChunk;
public:
FixedAllocator();
~FixedAllocator();
void init(size_t blockSize, size_t pageSize);
const int blockOwner(void* p) const;
void * allocate();
void deallocate(void* p);
};
FixedAllocator::FixedAllocator()
:blockSize(0),
blocks(0),
chunks(0),
allocChunk(nullptr)
{
}
FixedAllocator::~FixedAllocator()
{
Chunks::iterator it;
for (it = chunks.begin(); it != chunks.end(); ++it)
{
it->release();
}
}
void FixedAllocator::init(size_t blockSize_, size_t pageSize)
{
blockSize = blockSize_;
size_t numBlocks = pageSize / blockSize;
blocks = static_cast<uchar>(numBlocks);
}
const int FixedAllocator::blockOwner(void* p) const
{
size_t chunkLen = blocks * blockSize;
std::vector<int>::size_type i = 0, sz = chunks.size();
for (; i < sz; i++)
{
if (chunks[i].hasBlock(p, chunkLen))
{
return i;
}
}
return -1;
}
void* FixedAllocator::allocate()
{
if (!allocChunk || allocChunk->blocksAvailable == 0)
{
Chunks::iterator i = chunks.begin();
for (;;++i)
{
if (i == chunks.end())
{
// allocate memory for one more chunk
chunks.reserve(chunks.size() + 1);
Chunk newChunk;
newChunk.init(blockSize, blocks);
// add new chunk to memory pool
chunks.push_back(newChunk);
// points to new just initiated chunk
allocChunk = &chunks.back();
break;
}
if (i->blocksAvailable > 0)
{
// points to chunk with available blocks
allocChunk = &*i;
break;
}
}
}
return allocChunk->allocate(blockSize);
}
void FixedAllocator::deallocate(void* p)
{
// TODO. Optimize. Now very bruteforce and non-efficient
const int chunkPos = blockOwner(p);
if (chunkPos != -1)
{
Chunk chunk = chunks[chunkPos];
chunk.deallocate(p, blockSize);
// if chunk is releasable, release memory
if (chunk.releasable(blocks))
{
chunk.release();
chunks.erase(chunks.begin() + chunkPos);
// allocChunk may point to deleted chunk
// so, reset it
allocChunk = &chunks.back();
} else {
// there are free blocks in chunk
// so, reset allocChunk for faster future allocation
allocChunk = &chunk;
}
}
}
int main() {
FixedAllocator* alloc = new FixedAllocator();
alloc->init(4, 12);
void* p = alloc->allocate();
void* q = alloc->allocate();
void* r = alloc->allocate();
alloc->deallocate(p);
alloc->deallocate(q);
alloc->deallocate(r);
return 0;
}
如您所见,我的代码中有两个调试语句。一个在递增后打印blocksAvailable
值,另一个在递减后打印其值。
但是当我编译并运行我的代码时,这就是我在屏幕上的内容:
如您所见,blocksAvailable
以值3
启动,然后递减三次(三次调用allocate
方法),但每次递减后(调用{{ 1}})它的值保持不变 - deallocate
。它真的让我发疯,看起来像我的代码中的鬼。您可以轻松地重现它,编译和运行,如下所示:
1
我希望,有人可以帮助我找到这个幽灵出现的地方。
答案 0 :(得分:2)
以下是代码中唯一可以调用Chunk::deallocate
的地方:
Chunk chunk = chunks[chunkPos];
chunk.deallocate(p, blockSize);
第一行会复制Chunk
;第二行在其上调用deallocate
,其增加chunk.blocksAvailable
。但chunk
只是数据的副本。修改它没有持久的效果。
特别是,chunks[chunkPos]
不受影响且仍包含blocksAvailable = 0
。