I've been working on a memory pool allocator class, and no major problems have arisen, sans Visual Studio's debug assertion (_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)) being thrown whenever I try to free the memory allocated by new with delete.
typedef uintptr_t uptr;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
struct FreeList
{
FreeList *next;
};
template<class T, u8 alignment, u32 poolSize>
class PoolAllocator
{
private:
u8 _paddedSize; // The size in bytes of each allocated chunk of memory.
u32 _numAllocations;// The number of allocations made by the pool.
u32 _freeBytes; // The number of bytes left in the pool.
u32 _usedBytes; // The number of bytes currently occupied in the pool.
FreeList* _freeListHead; // A pointer to a freed space in memory.
public:
PoolAllocator() :
_paddedSize((sizeof(T) > sizeof(uptr) ? sizeof(T) : sizeof(uptr))),
_numAllocations(0),
_freeBytes(0),
_usedBytes(0),
_freeListHead(nullptr)
{
_freeListHead = reinterpret_cast<FreeList*>(operator new (_paddedSize * poolSize));
_freeBytes = _paddedSize * poolSize;
uptr current = reinterpret_cast<uptr>(_freeListHead);
uptr last = current + (_paddedSize * poolSize);
for (int i = 0; i < poolSize-1; i++)
{
uptr next = current + _paddedSize;
(reinterpret_cast<FreeList*>(current))->next = reinterpret_cast<FreeList*>(next);
current += _paddedSize;
}
reinterpret_cast<FreeList*>(current)->next = nullptr;
}
T *allocate()
{
if (_freeListHead != nullptr && _freeBytes >= _paddedSize) // Make sure the pool has memory left
{
uptr *toReturn = reinterpret_cast<uptr*>(_freeListHead); // Cast the pointer to a modifiable data type.
_freeListHead = _freeListHead->next; // VITAL THAT THIS IS BEFORE SETTING DATA TO 0.
*toReturn = 0; // Set the data at the memory location to 0.
_freeBytes -= _paddedSize;
_usedBytes += _paddedSize;
_numAllocations++;
printf("Allocated %d bytes of memory at %p.\n", _paddedSize, toReturn);
return reinterpret_cast<T*>(toReturn);
}
else
{
printf("Pool allocator out of memory! Returning nullptr.\n");
return nullptr;
}
}
void free(T **ptr)
{
FreeList *newHead = reinterpret_cast<FreeList*>(*ptr);
*ptr = nullptr;
newHead->next = _freeListHead;
_freeListHead = newHead;
_freeBytes += _paddedSize;
_usedBytes -= _paddedSize;
_numAllocations--;
printf("Freed %d bytes of memory at %p.\n", _paddedSize, _freeListHead);
}
void clear()
{
assert(_usedBytes == 0);
FreeList *head = _freeListHead;
while (head != 0)
{
FreeList *next = head->next;
delete reinterpret_cast<T*>(head);
head = next;
}
_paddedSize = 0;
_numAllocations = 0;
_freeBytes = 0;
_usedBytes = 0;
_freeListHead = nullptr;
}
};
The test code I am using:
int main()
{
PoolAllocator<int, 4, 4> pool;
int *a, *b, *c, *d, *e;
a = pool.allocate();
b = pool.allocate();
c = pool.allocate();
d = pool.allocate();
pool.free(&a);
e = pool.allocate();
printf("A | %p\t%d\nB | %p\t%d\nC | %p\t%d\nD | %p\t%d\nE | %p\t%d\n", a, 0, b, *b, c, *c, d, *d, e, *e);
pool.free(&b);
pool.free(&c);
pool.free(&d);
pool.free(&e);
pool.clear();
return 0;
}
The problem area lies here:
void clear()
{
assert(_usedBytes == 0);
FreeList *head = _freeListHead;
while (head != 0)
{
FreeList *next = head->next;
delete reinterpret_cast<T*>(head); // Debug assert
head = next;
}
_paddedSize = 0;
_numAllocations = 0;
_freeBytes = 0;
_usedBytes = 0;
_freeListHead = nullptr;
}
What this code is supposed to do is increment through the linked list of memory locations where individual pieces of data of type T are being stored. My reasoning for this being a valid piece of code is that, because the initially allocated memory is divided into pieces the size of sizeof(T). Therefor, I assumed that it would be appropriate to typecast the memory address to a pointer of type T* so that the individual block of memory would be completely deallocated. This would be done for the entire linked list, ensuring that all allocated memory is freed. However, when I run the code, it always throws the assert on the delete.
Stepping through the code showed that after the first iteration of typecasting and deleteing, the pointers take on strange values (the next variable, for instance:
next 0x004b2864 {next=0xfeeefeee {next=??? } } FreeList *
Whereas before, it was
next 0x006c2864 {next=0x006c2860 {next=0x006c285c {next=0x00000000
} } } FreeList *
just as it should be). I have tried this approach many different times in many different ways, including typecasting to void* instead of T*.
This problem has had me stumped for days, and any help would be appreciated. Thank you.
reinterpret_cast<T*>(head)->~T();
I tested this and not getting the assert anymore by replacing delete reinterpret_cast(head);
You can't call delete on head because the memory wasn't allocated by calling new T();
This will not compile:
struct FreeList
{
FreeList *next;
};
... but the following will:
struct FreeList
{
struct FreeList *next;
};
Otherwise, an error about incomplete struct definition or unknown type FreeList will be raised by the compiler.
Related
I am making my own dynamic allocator in C++. But I ran into a problem where I can't free my memory.
This is the code for Test.cpp:
#include "Memory/MemoryManager.h"
int main(){
initMemory(1);
int* p = allocate<int>();
int* q = allocate<int>();
int* r = allocate<int>();
cout<<p<<endl;
cout<<q<<endl;
cout<<r<<endl;
freeAddress<int>(q);
return 0;
}
MemoryManager.h:
#ifndef MEMORY_MANAGER_INCLUDED
#define MEMORY_MANAGER_INCLUDED
#include <iostream>
#include <map>
#include <memory>
using namespace std;
char* memory;
char* current;
map<void*, size_t> freePointers;
void initMemory(size_t size){
memory = (char*)malloc(size);
current = memory;
}
template<typename T> T* allocate(){
T* address = NULL;
for (auto p : freePointers){
if (p.second == sizeof(T)){
address = static_cast<T*>(p.first);
}
}
if (address == NULL){
address = new(current) T();
current += sizeof(T);
}
return address;
}
template<typename T> T* allocate(size_t size){
T* address = NULL;
for (auto p : freePointers){
if (p.second == sizeof(T) * size){
return static_cast<T*>(p.first);
}
}
if (address == NULL){
address = new(current) T[size];
current += sizeof(T) * size;
}
return address;
}
template<typename T> void freeAddress(T* address){
freePointers.insert({(void*)address, sizeof(*address)});
delete address;
}
template<typename T> void freeAddress(T* address, size_t size){
freePointers.insert({(void*)address, sizeof(*address) * size});
delete [] address;
}
#endif
Output:
0x55ee37729e70
0x55ee37729e74
0x55ee37729e78
0x55ee37729e70
0x55ee37729e78
free(): invalid pointer
I know that I can't delete pointers on stack memory but I am not using it at all. Also please point out if I am doing something wrong or performance expensize. Please help.
You allocate a large chunk of memory with malloc, then call delete on some pointer into the allocated memory.
In your code, q is (memory + sizeof(int)). This pointer has never been returned by an allocation and therefore cannot be freed. This is the reason for the error.
Moreover, mismatching malloc with delete is undefined behavior. You should instead call free() on the original memory pointer as cleanup in the end, and never call delete on individual values.
If you want to call the destructor in freeAddress, use address->~T();.
You are allocating a single 1-byte block of memory one time, in your initMemory(). You are using malloc() for that allocation, so that memory block must be freed with free() when you are done using it, but you are not doing that.
Your freeAddress() is delete'ing memory that was never allocated with new. Inside of allocate(), you are using placement-new instead of new, they are not the same thing. When you use placement-new, you have to call an object's destructor manually, not free() or delete it.
And you certainly do not want to free memory that you are going to reuse later. That defeats the whole purpose of caching "freed" memory.
And new[] uses more memory than you request, so that it can store info for delete[] to know know how many elements to free, and how to free them. You won't know how much extra overhead that is, as it is implementation defined. So it is not safe to use new[] in this situation.
Try something more like this instead
#include "Memory/MemoryManager.h"
int main(){
initMemory(sizeof(int) * 3);
int* p = allocate<int>();
int* q = allocate<int>();
int* r = allocate<int>();
cout << p << endl;
cout << q << endl;
cout << r << endl;
freeAddress<int>(q);
doneMemory();
return 0;
}
#ifndef MEMORY_MANAGER_INCLUDED
#define MEMORY_MANAGER_INCLUDED
#include <iostream>
#include <map>
#include <memory>
char* memory;
char* current;
size_t available;
std::map<void*, size_t> freePointers;
void initMemory(size_t size){
memory = (char*) malloc(size);
current = memory;
available = (memory) ? size : 0;
}
void doneMemory(){
freePointers.clear();
free(memory);
memory = current = nullptr;
available = 0;
}
template<typename T>
T* allocate(){
T *address = nullptr;
for (auto iter = freePointers.begin(); iter != freePointers.end(); ++iter){
if (iter->second == sizeof(T)){
address = static_cast<T*>(iter->first);
freePointers.erase(iter);
break;
}
}
if (!address){
if (available < sizeof(T)){
return nullptr;
}
address = static_cast<T*>(current);
current += sizeof(T);
available -= sizeof(T);
}
return new(address) T();
}
template<typename T>
T* allocate(size_t count){
T *address = nullptr;
size_t size = count * sizeof(T);
for (auto iter = freePointers.begin(); iter != freePointers.end(); ++iter){
if (iter->second == size){
address = static_cast<T*>(iter->first);
freePointers.erase(iter);
break;
}
}
if (!address){
if (available < size){
return nullptr;
}
address = static_cast<T*>(current);
current += size;
available -= size;
}
for(size_t i = 0; i < count; ++i)
new(address+i) T();
}
return address;
}
template<typename T>
void freeAddress(T* address){
address->~T();
freePointers.insert({(void*)address, sizeof(T)});
}
template<typename T>
void freeAddress(T* address, size_t count){
for (size_t i = 0; i < count; ++i)
address[i].~T();
freePointers.insert({(void*)address, sizeof(T) * count});
}
#endif
That being said, this isn't a very safe or robust memory allocator, but it should get you started.
If you really want to create a custom memory allocator, you should write a class that follows the Allocator strategy, and then you can use that allocator with standard C++ containers, like std::vector. Let the compiler and standard library handle most of the hard work for you.
I'm working on a project which I have to implement new operator and delete operator, and manage my memory by my own MemoryManager - which has Lists of available memory.
In order to allocate my lists and nodes (which should not need to be managed), I should invoke operator new explicitly after calling malloc.
When I'm trying to call a function - setNext(), it throw an exception:Exception: EXC_BAD_ACCESS (code=1, address=0x0)
Creating HashTable of LinkedLists:
MyHashTable::MyHashTable(size_t memorySize, void* startingPtr)
:size(getLowerLog(memorySize) + 1), lists((LinkedList**)malloc(sizeof(LinkedList*) * size)), startingPtr(startingPtr) {
for (size_t i = 0; i < size; i++) {
auto memSize = (size_t) pow(2, i);
void * l = malloc(sizeof(LinkedList));
lists[i] = new (l) LinkedList(memSize);
// Placement new
}
dividingMemory(memorySize, startingPtr);
}
The dividingMemory function does:
void MyHashTable::dividingMemory(size_t memorySize, void* startingPtr) {
while (memorySize > 0) {
size_t memPow = getLowerLog(memorySize);
auto max = (size_t) pow(2, memPow);
lists[max]->add(ptr); // here is the call to LinkedList::add()
startingPtr = ((char*) startingPtr) + max;
memorySize -= max;
}
}
LinkedList::add() :
void LinkedList::add(void * ptr) {
void* p = malloc(sizeof(Node));
Node * newNode = new (p) Node(ptr);
// Placement new
newNode->setNext(head);
std::cout << "haha" << std::endl;
head = newNode;
size++;
}
Whole Node class:
Node.h:
#ifndef EX3_NODE_H
#define EX3_NODE_H
#include <iostream>
#include <string>
class Node {
private:
void* ptr;
Node* next;
public:
explicit Node(void*);
inline Node* getNext() const {
return next;
}
inline void setNext(Node* next) {
this->next = next;
}
~Node() = default;
};
#endif //EX3_NODE_H
Node.cpp:
Node::Node(void * ptr):ptr(ptr) { }
I've tried to call another function (toString) and it secceded.
What am I doing wrong?
I tried #Ben Voigt's answer but it didn't solve it.
You are throwing away the return value of placement new, which is the only formally correct way of getting a pointer to the newly constructed object. You then call member functions on the raw-storage pointer you passed into placement new. Don't do that.
This is the correct way to use malloc together with placement new:
void* rawBlock = malloc(sizeof(Node));
Node* newNode = new (rawBlock) Node(ptr);
// later
newNode->~Node();
free(rawBlock);
Logic mistake, I've tried to access an undefined memory location.
auto max = (size_t) pow(2, memPow);
lists[max]->add(ptr);
Should be:
auto max = (size_t) pow(2, memPow);
lists[(log(max) / log(2)]->add(ptr);
I'm attempting to write a simple pool allocator for allocation and deallocation in c++ as part of a uni course task. We were given a reference to to git-user floooh's oryol engine, https://github.com/floooh/oryol/blob/master/code/Modules/Core/Memory/poolAllocator.h, hence I try to do something similar by splitting the pool into puddles that are allocated when needed. Starting with one and then incrementing as memory demands increase.
Each puddle in my case maintains its own free list of nodes and I fail already in creating the first puddle: I get segmentation fault when I try to access node struct data members. Below is my pool allocator class description along with constructor and function for adding a puddle. I commented in caps lock "SEGMENTATION FAULT" in allocNewPuddle() where it fails, line 10 in that function.
Class description:
template<class T> class memAllocator {
public:
memAllocator();
~memAllocator();
struct Puddle;
struct mNode {
mNode* nextN;
mNode* prevN;
uint puddle;
};
struct Puddle {
mNode* headN_free;
mNode* headN_occ;
};
uint numPuddles;
static const uint nodesInPuddle = 512;
static const uint maxPuddles = 512;
Puddle* puddles[maxPuddles];
uint nodeSize;
uint elemSize;
uint puddleStructSize;
void allocNewPuddle();
void* allocate();
void deallocate(void* obj);
void* findNextFreeNode();
template<typename... ARGS> T* create(ARGS&&... args);
void destroy(T* obj);
};
Constructor:
template<class T>
memAllocator<T>::memAllocator() // creates instance of allocator starting with one puddle allocated
{
this->numPuddles = 0;
this->nodeSize = sizeof(mNode);
this->elemSize = nodeSize + sizeof(T);
this->puddleStructSize = sizeof(Puddle);
allocNewPuddle();
}
Add a new puddle:
template<class T>
void memAllocator<T>::allocNewPuddle() // allocates a new puddle
{
// allocate memory for one puddle
assert(numPuddles < maxPuddles);
Puddle* newPuddle = (Puddle*) malloc(puddleStructSize + nodesInPuddle * elemSize);
// allocate nodes in free list pointed to by puddle struct
newPuddle->headN_free = (mNode*) (newPuddle + puddleStructSize + (nodesInPuddle-1)*elemSize);
for (int i = nodesInPuddle-2; i >= 0; i--) {
mNode* curNode = (mNode*) (newPuddle + puddleStructSize + i*elemSize);
// Fails here when attempting to access mNode struct members
curNode->puddle = numPuddles; // SEGMENTATION FAULT HERE ON FIRST ITERATION
curNode->prevN = nullptr;
curNode->nextN = newPuddle->headN_free;
curNode->nextN->prevN = curNode;
newPuddle->headN_free = curNode;
}
newPuddle->headN_occ = nullptr;
puddles[numPuddles] = newPuddle;
numPuddles++;
}
Here is my main.cc:
#include "memAllocator.h"
#include <iostream>
class Test {
public:
Test();
~Test();
int arr[5];
};
Test::Test() {
for (int i = 0; i < 5; i++) {
this->arr[i] = i;
}
}
Test::~Test() {
std::cout << "destructor called" << std::endl;
}
int main(int argc, char* argv[]) {
memAllocator<Test> memPool = memAllocator<Test> ();
Test* test = memPool.create();
for (int i = 0; i < 5; i++) {
std::cout << test->arr[i] << std::endl;
}
memPool.destroy(test);
for (int i = 0; i < 5; i++) {
std::cout << test->arr[i] << std::endl;
}
}
My guess is that I am doing something horribly naïve with c++ pointers, but from what I know the above should work. If not then I look forward to a good scolding.
Oh, and as you can see I'm not bothering to align memory since it is a small assignment, and as I understand this is not essential for it to work it only makes it faster, but is it possible this can cause wrong memory to be read and written to as more is demanded?
You have incorrect address calculation in line
mNode* curNode = (mNode*) (newPuddle + puddleStructSize + i*elemSize);
newPuddle is Puddle pointer, but you trying to add bytes. Thus you have new address far beyond end of allocated memory buffer. So you must add explicit cast to byte pointer (char, uint8_t etc)
mNode* curNode = (mNode*) ((char*)newPuddle + puddleStructSize + i*elemSize);
You must fix this line too
newPuddle->headN_free = (mNode*) (newPuddle + puddleStructSize + (nodesInPuddle-1)*elemSize);
Here's my code:
template<class T> class Test
{
public:
int Size = 0;
int Length = 0;
T* Items;
Test() {}
~Test()
{
delete [] Items;
}
void Append(const T& newItem)
{
if (Size + 1 >= Length)
{
Length += 250;
T* old = Items;
Items = new T[Length + 250];
for (int i = 0; i < Size; i++)
Items[i] = old[i];
delete [] old;
}
Items[Size] = newItem;
Size++;
}
};
Test<int> test;
for (int i = 0; i < 500000; i++)
test.Append(i);
I'm populating the dynamic array with 500000 integers which must take just 1-2Mb but it takes about 30Mb. There's no problem if i set the initial size to 500000(i.e. no resizing occurring). The grow value(250) seems to affect the memory somehow, if it's larger(for example 1000) then the memory usage is pretty low. What's wrong?
Typically, when you are reallocating an array, you do not want to modify the actual array until the very last second (to maintain exception safety):
T* temp = new T[new_size];
// assume count is the previous size and count < new_size
std::copy(Items, Items + count, temp);
std::swap(temp, Items);
delete [] temp;
Aside from that, there is nothing visible in your code that would cause a memory leak.
The extra size can possibly be due to other optimizations (being turned off) and/or debugging symbols being turned on. What compiler options are you using (and what compiler)? It should be noted that extra size is not necessarily an indication of a memory leak. Have you run this in a debugger or memory profiler which found a leak?
It should also be noted that std::vector does all of this for you.
Looking at your code, you're going to segfault more so than leak memory due to the fact that calling delete or delete[] on a non-NULL, but previously deallocated, pointer is a Bad Thing. Also, I don't believe this is your real code, because what you posted won't compile.
When you delete a pointer, always set it to NULL afterwards. It's good practice to initialize to NULL as well. Let's fix up your code to make sure we don't call delete on previously deallocated pointers. Also, let's initialize our pointer to NULL.
Your misuse of memory probably stems from the following lines of code:
Length += 250;
T* old = Items;
Items = new T[Length + 250];
Notice that you increment Length by 250, but then allocate Length+250 more elements? Let's fix that, too.
template<class T>
class Test
{
public:
int Size;
int Length;
T* Items;
Test() : Size(0), Length(0), Items(NULL){}
~Test() {
if (Items != NULL)
delete [] Items;
}
void Append(const T& newItem)
{
if (Size + 1 >= Length)
{
Length += 250;
T* old = Items;
Items = new T[Length];
for (int i = 0; i < Size; i++)
Items[i] = old[i];
delete [] old;
old = NULL;
}
Items[Size] = newItem;
Size++;
}
};
int main(){
Test<int> test;
for (int i = 0; i < 500000; i++)
test.Append(i);
}
I have to work and create often matrices(I have to use pointers) so I made a function in C++ to allocate space for them and also make sure that the last value is set to NULL.
The application drops this error(glibc detected: memory curruption) in a specific case. Here is the code:
template<typename T> T *allocate(int size) {
T *temp = new T[size];
temp[size] = (T) NULL;
return temp;
}
This works:
unsigned char *tmp = allocate <unsigned char> (10);
But this one drops the error:
unsigned char **tmp = allocate <unsigned char *> (10);
That would be the equivalent of:
unsigned char **tmp = new unsigned char *[10];
tmp[10] = (unsigned char *) NULL;
Which is good. Why would it drop me this error?
Update: Thanks for the responses. I am so blind. That's one bug. But the problem of the crash was from another part of the code but also because I was adding NULL outside the allocated space of the array.
You can't do this:
temp[size] = (T) NULL;
Size in this case is indexing the memory position AFTER the last one you allocated, change it for this:
temp[size-1] = (T) NULL;
temp[size] = (T) NULL;
is assigning to unallocated memory.
T *temp = new T[size];
allocates from temp[0] to temp[size-1]