背景:
为了更好地熟悉C ++中的内存管理,我最近决定编写自己的内存管理库。
目前,这个库并不包含基本池分配器以外的其他功能,其整体足够小,可以在下面内联:
PoolAllocator::PoolAllocator (const AllocatorConfig& config)
{
this->nextAddress = 0;
this->size = config.AllocatorSize;
this->memoryArray = new byte[this->size];
}
PoolAllocator::~PoolAllocator ()
{
delete[] this->memoryArray;
}
void * PoolAllocator::Allocate (size_t size)
{
void* pointer = &(this->memoryArray[this->nextAddress]);
this->nextAddress += (unsigned long)size;
return pointer;
}
void PoolAllocator::Delete (void * object)
{
this->nextAddress -= sizeof (object);
}
然后我通过替换全局new来使用allocator来使用allocator(如果它存在,否则它只使用malloc,这是在构造allocator本身时调用的)。
问题:
在测试这个类时,我编写了一个简单的应用程序来分配一个std :: string对象,如下所示:
#include <string>
#include <iostream>
#include "PoolAllocator.hpp"
#include "AllocatorConfig.hpp"
PoolAllocator *allocator;
int main (int numArgs, char *args[])
{
AllocatorConfig config = AllocatorConfig ();
config.AllocatorSize = 2048;
allocator = new PoolAllocator (config);
std::string *s = new std::string();
std::cout << "String object size: " << sizeof (*s) << std::endl;
*s = "test";
char *charBuffer = &((*s)[0]);
std::cout << "First allocate offset: " << (int)((int)&((*s)[0]) - (int)s) << std::endl;
std::cout << "Original cstring: " << charBuffer << std::endl;
*s = "thisIsALongStringToTestTheAddress";
std::cout << "Second allocate offset: " << (int)((int)&((*s)[0]) - (int)s) << std::endl;
std::cout << "Original cstring: " << charBuffer << std::endl;
delete allocator;
}
void * operator new(size_t size)
{
if (allocator)
{
return allocator->Allocate(size);
}
else
{
return malloc (size);
}
}
输出如下:
String object size: 28
First allocate offset: 4
Original cstring: test
Second allocate offset: 36
Original cstring: ,ÀH
这开始看起来相当合理,因为std :: string内部指向的字符串原语需要是连续的,而std :: string实际上无法知道它可以简单地扩展到相邻的连续空间
无论其
当替换全局delete
和delete[]
并附加调试器时,我从未见过任何被调用的证据。我也试图打破free
,但那也不起作用,所以......
问题:
使用该内存的std :: string是什么?我原以为它会调用delete[]
所以我的分配器可以收回它,但据我所知,它根本不会释放它。它只是在那个地方喷洒堆积并坚持它直到它死亡,或者是否有其他东西在这里发生我不知道?
答案 0 :(得分:3)
您所看到的是短字符串优化,其中字符串对象直接在其自己的分配中存储非常小的字符串,因此在重新分配时没有任何内容可以解除分配。
请参阅this example,我们在那里进行了类似的测试,结果如下:
declaration
assignment of 'test'
assignment of 'thisIsALongStringToTestTheAddress'
allocate(34) = 0xb7ac30
assignment of 'thisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddress'
allocate(133) = 0xb7ac60
deallocate(0xb7ac30, 34)
scope end
deallocate(0xb7ac60, 133)
解释这个:
"test"
时,没有分配。但是等等,为什么你没有看到你分配的较长字符串的重新分配?好吧,你永远不会delete s;
,所以你的程序终止于泄漏的分配,操作系统将自行清理。在delete s;
之前添加delete allocator;
,您应该会在operator delete
中看到已释放的分配。
测试的源代码:
#include <iostream>
#include <string>
template <typename T, typename Allocator = std::allocator<T>>
class AllocatorProxy
{
private:
Allocator proxy;
public:
using pointer = typename Allocator::pointer;
using const_pointer = typename Allocator::const_pointer;
using value_type = typename Allocator::value_type;
using size_type = typename Allocator::size_type;
using difference_type = typename Allocator::difference_type;
pointer allocate(size_type n)
{
pointer p = proxy.allocate(n);
std::cout << "allocate(" << n << ") = " << static_cast<void *>(p) << '\n';
return p;
}
pointer allocate(size_type n, void const *l)
{
pointer p = proxy.allocate(n, l);
std::cout << "allocate(" << n << ", " << l << ") = " << static_cast<void *>(p) << '\n';
return p;
}
void deallocate(pointer p, size_type n) noexcept
{
std::cout << "deallocate(" << static_cast<void *>(p) << ", " << n << ")\n";
proxy.deallocate(p, n);
}
size_type max_size()
{
return proxy.max_size();
}
};
using astring = std::basic_string<char, std::char_traits<char>, AllocatorProxy<char>>;
int main() {
std::cout << "declaration\n";
astring str;
std::cout << "assignment of 'test'\n";
str.assign("test");
std::cout << "assignment of 'thisIsALongStringToTestTheAddress'\n";
str.assign("thisIsALongStringToTestTheAddress");
std::cout << "assignment of 'thisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddress'\n";
str.assign("thisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddress");
std::cout << "scope end\n";
return 0;
}