Heap array allocates 4 extra bytes if class has destructor - c++

I'm new to C++ and I've been playing around with memory allocation lately. I have found out that when you declare a class with a destructor, like so:
class B
{
public:
~B() { }
};
And then create a heap array of it like so:
B* arr = new B[8];
The allocator allocates 12 bytes but when I remove the destructor, it only allocates 8 bytes. This is how I'm measuring the allocation:
size_t allocated = 0;
void* operator new(size_t size)
{
allocated += size;
return malloc(size);
}
void deallocate(B* array, size_t size)
{
delete[] array;
allocated -= size * sizeof(B);
}
Of course I have to call deallocate manually while the new operator is called automatically.
I have found this problem while working with an std::string* and I realised that the deallocator worked fine with an int* but not with the former.
Does anyone know why that happens and more importantly: How to detect these programmatically at runtime?
Thanks in advance.

You are looking at an implementation detail of how your compiler treats new[] and delete[], so there isn't a definitive answer for the extra space being allocated since the answer will be specific to an implementation -- though I can provide a likely reason below.
Since this is implementation-defined, you cannot reliably detect this at runtime. More importantly, there shouldn't be any real reason to do this. Especially if you're new to C++, this fact is more of an interesting/esoteric thing to know of, but there should be no real benefit detecting this at runtime.
It's important to also be aware that this only happens with array-allocations, and not with object allocations. For example, the following will print expected numbers:
struct A {
~A(){}
};
struct B {
};
auto operator new(std::size_t n) -> void* {
std::cout << "Allocated: " << n << std::endl;
return std::malloc(n);
}
auto operator delete(void* p, std::size_t n) -> void {
std::free(p);
}
auto main() -> int {
auto* a = new A{};
delete a;
auto* b = new B{};
delete b;
}
Output:
Allocated: 1
Allocated: 1
Live Example
The extra storage only gets allocated for types with non-trivial destructors:
auto* a = new A[10];
delete[] a;
auto* b = new B[10];
delete[] b;
Outputs:
Allocated: 18
Allocated: 10
Live Example
The most likely reason why this happens is that extra bookkeeping of a single size_t is being kept at the beginning of allocated arrays containing non-trivial destructors. This would be done so that when delete is called, the language can know how many objects require their destructors invoked. For non-trivial destructors, its able to rely on the underlying delete mechanics of their deallocation functions.
This hypothesis is also supported by the fact that for the GNU ABI, the extra storage is sizeof(size_t) bytes. Building for x86_64 yields 18 for an allocation of A[10] (8 bytes for size_t). Building for x86 yields 14 for that same allocation (4 bytes for size_t).
Edit
I don't recommend doing this in practice, but you can actually view this extra data from arrays. The allocated pointer from new[] gets adjusted before being returned to the caller (which you can test by printing the address from the new[] operator).
If you read this data into a std::size_t, you can see that this data -- at least for the GNU ABI -- contains the exact count for the number of objects allocated.
Again, I do not recommend doing this in practice since this exploits implementation-defined behavior. But just for fun:
auto* a = new A[10];
const auto* bytes = reinterpret_cast<const std::byte*>(a);
std::size_t count;
std::memcpy(&count, bytes - sizeof(std::size_t), sizeof(std::size_t));
std::cout << "Count " << count << std::endl;
delete[] a;
The output:
Count 10
Live Example

Related

C++ placement new, Invalid read and InvalidInvalid free() / delete / delete[] / realloc()

I'm experimenting the usage of placement new to try to understand how it works.
Executing the code below:
#include <iostream>
#define SHOW(x) std::cout << #x ": " << x << '\n'
template<typename T>
static T *my_realloc(T *ptr, size_t count) {
return (T *) realloc((void *) ptr, count * sizeof(T));
}
template<typename T>
static void my_free(T *ptr) {
free((T *) ptr);
}
int main() {
constexpr int count = 40;
int cap = 0;
int size = 0;
std::string *strs = nullptr;
auto tmp_str = std::string();
for(int i = 0; i < count; i++) {
tmp_str = std::to_string(i);
if(size == cap) {
if(cap == 0)
cap = 1;
else
cap *= 2;
strs = my_realloc(strs, cap);
}
new (&strs[size++]) std::string(std::move(tmp_str));
}
for(int i = 0; i < size; i++)
SHOW(strs[i]);
std::destroy_n(strs, size);
my_free(strs);
return 0;
}
I get the errors:
Invalid read of size 1
Invalid free() / delete / delete[] / realloc()
Removing the line
std::destroy_n(strs, size);
The error of invalid free is solved, but somehow all memory of the program is freed and no leaks are generated. But i can't find how the std::string destructor is called in the program.
If you want to store non-trivial types (such as std::string), then realloc simply cannot be used. You will find that standard library containers like e.g. std::vector will also not use it.
realloc may either extend the current allocation, without moving it in memory, or it might internally make a new allocation in separate memory and copy the contents of the old allocation to the new one. This step is performed as if by std::memcpy. The problem here is that std::memcpy will only work to actually create new objects implicitly in the new allocation and copy the values correctly if the type is trivially-copyable and if it and all of its subobjects are implicit-lifetime types. This definitively doesn't apply to std::string or any other type that manages some (memory) resource.
You are also forgetting to check the return value of realloc. If allocation failed, it may return a null pointer, which you will then use regardless, causing a null pointer dereference, as well as a memory leak of the old allocation.
Instead of using realloc you should make a new allocation for the new size, then placement-new copies of the objects already in the old allocation into the new one and then destroy the objects in the old allocation and free it.
If you want to guarantee that there won't be memory leaks when exceptions are thrown things become somewhat complicated. I suggest you look at the std::vector implementation of one of the standard library implementations if you want to figure out the details.
strs = my_realloc(strs, cap);
strs is a pointer to a std::string, and this will result in the contents of the pointer to be realloc()ed.
This is undefined behavior. C++ objects cannot be malloced, realloced, or freeed. Using a wrapper function, or placement new, at some point along the way, does not change that.
Everything from this point on is undefined behavior, and the resulting consequences are of no importance.

The sized operator delete[] is never called

I am trying to keep track of how much memory has been allocated in my developments. It is easy to keep track of allocation because the overload of void *operator new (size_t) and void *operator new[](size_t) allow to track how much is allocated.
With C++ < C++14, one can resort to a technique of over-allocating memory to store the size of the allocation
Since C++14, there are corresponding void operator delete(void*p, size_t size) and void operator delete[](void*p, size_t size) that should allow to account accurately for every de-allocation (except for a delete of an incomplete type, which is then left to the implementation).
However, though the first version is being called by g++ where a call to delete a single object is made, I have not found a single compiler calling the second one. Here is my test code:
#include <iostream>
size_t currentAlloc;
void * operator new(size_t size)
{
currentAlloc += size;
std::cout << "1\n";
return malloc(size);
}
void *operator new[](size_t size)
{
std::cout << "3\n";
currentAlloc += size;
return malloc(size);
}
void operator delete(void *p) noexcept
{
std::cout << "Unsized delete\n";
free(p);
}
void operator delete(void*p, size_t size) noexcept
{
std::cout << "Sized delete " << size << '\n';
currentAlloc -= size;
free(p);
}
void operator delete[](void *p) noexcept
{
std::cout << "Unsized array delete\n";
free(p);
}
void operator delete[](void*p, std::size_t size) noexcept
{
std::cout << "Sized array delete " << size << '\n';
currentAlloc -= size;
free(p);
}
int main() {
int *n1 = new int();
delete n1;
int *n2 = new int[10];
delete[] n2;
std::cout << "Still allocated: " << currentAlloc << std::endl;
}
Compiled with g++ -std=c++14 test.C or clang++ -std=c++14 test.C. The result of which outputs for g++:
1
Sized delete 4
3
Unsized array delete
Still allocated: 40
I was expecting for the sized array delete to be called for the second delete and for the last printed value to be 0 instead of 40. clang++ does not call any sized de-allocation and neither does the Intel compiler.
Is my code incorrect in any way? Am I misunderstanding the standard? Or are both g++ and clang++ not following the standard?
According to cppreference.com, which is usually reliable, it's unspecified which version is called "when deleting objects of incomplete type and arrays of non-class and trivially-destructible class types" (my emphasis).
It also seems that compilers disable the sized delete by default.
The purpose of the sized deallocation API isn't to help you track how much memory has been allocated or deallocated, and it can't be used reliably for that anyway. The purpose of the sized deallocation API is to improve the efficiency of memory allocators that support sized deallocations, because it lets the compiler call a sized-deallocation method in some cases which means that the memory allocator doesn't need to look up the size of deallocated pointer when doing the deallocation. Andrei Alexandrescu talks about this a bit in his 2015 CppCon talk about the std::allocator API.
Most memory allocators provide an API like mallinfo(3) that let you explicitly query the allocator for statistics about how much memory has been allocated or deallocated; I would recommend reading the documentation for whatever allocator you're using to see how to access these statistics.
The reason you can't use it to track the total size of all deallocations is that in general, the compiler doesn't always know the size of the objects that are begin deleted. Consider for example the following code:
char *foo(size_t n) {
char *p = new char[n];
// do stuff with p, maybe fill it in
return p;
}
void bar(char *p) {
delete[] p;
}
void quux(size_t nbytes) {
char *p = foo(nbytes);
bar(p);
}
In this case the memory is allocated in one place but deallocated elsewhere, and the information about the size of the allocation is lost at the deallocation site. This specific example is very simple so an optimizing compiler might see through this example if the two functions are located nearby, but in general it is not guaranteed that the sized deallocation function will be used, it's something that the compiler may do.
Additionally, Clang currently (as of late 2020) does not enable sized deallocations even when compiling with -std=c++14 or (a later standards version like c++17 or c++20); currently to get it to used sized deallocations you need to add -fsized-deallocation to your clang command line.

Does c++ operator new[]/delete[] call operator new/delete?

Does c++ operator new[]/delete[] (not mine) call operator new/delete?
After I replaced operator new and operator delete with my own implemenation, then the following code will call them:
int *array = new int[3];
delete[] array;
And When I also replaced operator new[] and operator delete[], then the above code will call only them.
My operators implementation:
void *operator new(std::size_t blockSize) {
std::cout << "allocate bytes: " << blockSize << std::endl;
return malloc(blockSize);
}
void *operator new[](std::size_t blockSize) {
std::cout << "[] allocate: " << blockSize << std::endl;
return malloc(blockSize);
}
void operator delete(void *block) throw() {
int *blockSize = static_cast<int *>(block);
blockSize = blockSize - sizeof(int);
std::cout << "deallocate bytes: " << *blockSize << std::endl;
free(block);
}
void operator delete[](void *block) throw() {
int *blockSize = static_cast<int *>(block);
blockSize = blockSize - sizeof(int);
std::cout << "[] deallocate bytes: " << *blockSize << std::endl;
free(block);
}
I have a second question which maybe not so related, why the code prints:
[] allocate: 12
[] deallocate bytes: 0
Instead of this:
[] allocate: 16
[] deallocate bytes: 16
Since the allocation operators new and new[] pretty much do the same thing(a), it makes sense that one would be defined in terms of the other. They're both used for allocating a block of a given size, regardless of what you intend to use it for. Ditto for delete and delete[].
In fact, this is required by the standard. C++11 18.6.1.2 /4 (for example) states that the default behaviour of operator new[] is that it returns operator new(size). There's a similar restriction in /13 for operator delete[].
So a sample default implementation would be something like:
void *operator new(std::size_t sz) { return malloc(sz); }
void operator delete(void *mem) throw() { free(mem); }
void *operator new[](std::size_t sz) { return operator new(sz); }
void operator delete[](void *mem) throw() { return operator delete(mem); }
When you replace the new and delete functions, the new[] and delete[] ones will still use them under the covers. However, replacing new[] and delete[] with your own functions that don't call your new and delete results in them becoming disconnected.
That's why you're seeing the behaviour described in the first part of your question.
As per the second part of your question, you're seeing what I'd expect to see. The allocation of int[3] is asking for three integers, each four bytes in size (in you environment). That's clearly 12 bytes.
Why it seems to be freeing zero bytes is a little more complex. You seem to think that the four bytes immediately before the address you were given are the size of the block but that's not necessarily so.
Implementations are free to store whatever control information they like in the memory arena(b) including the following possibilities (this is by no means exhaustive):
the size of the current memory allocation;
a link to the next (and possibly previous) control block;
a sentinel value (such as 0xa55a or a checksum of the control block) to catch arena corruption.
Unless you know and control how the memory allocation functions use their control blocks, you shouldn't be making assumptions. For a start, to ensure correct alignment, control blocks may be padded with otherwise useless data. If you want to save/use the requested size, you'll need to do it yourself with something like:
#include <iostream>
#include <memory>
// Need to check this is enough to maintain alignment.
namespace { const int buffSz = 16; }
// New will allocate more than needed, store size, return adjusted address.
void *operator new(std::size_t blockSize) {
std::cout << "Allocating size " << blockSize << '\n';
auto mem = static_cast<std::size_t*>(std::malloc(blockSize + buffSz));
*mem = blockSize;
return reinterpret_cast<char*>(mem) + buffSz;
}
// Delete will unadjust address, use that stored size and free.
void operator delete(void *block) throw() {
auto mem = reinterpret_cast<std::size_t*>(static_cast<char*>(block) - buffSz);
std::cout << "Deallocating size " << *mem << '\n';
std::free(mem);
}
// Leave new[] and delete[] alone, they'll use our functions above.
// Test harness.
int main() {
int *x = new int;
*x = 7;
int *y = new int[3];
y[0] = y[1] = y[2] = 42;
std::cout << *x << ' ' << y[1] << '\n';
delete[] y;
delete x;
}
Running that code results in successful values being printed:
Allocating size 4
Allocating size 12
7 42
Deallocating size 12
Deallocating size 4
(a) The difference between new MyClass and new MyClass[7] comes later than the allocation phase, when the objects are being constructed. Basically, they both allocate the required memory once, then construct as many objects in that memory as necessary (once in the former, seven times in the latter).
(b) And an implementation is allowed to not store any control information inline. I remember working on embedded systems where we knew that no allocation would ever be more than 1K. So we basically created an arena that had no inline control blocks. Instead it had a bit chunk of memory, several hundred of those 1K blocks, and used a bitmap to decide which was in use and which was free.
On the off chance someone asked for more than 1K, the got NULL. Those asking for less than or equal to 1K got 1K regardless. Needless to say, it was much faster than the general purpose allocation functions provided with the implementation.

Header size of array in C++ for WDK

Not sure if "header" is the correct term, so please correct me if it isn't.
I'm trying to first allocate a memory, and then use an overloaded (placement) new[] operator to initialize an array of class objects (say, MyClass).
Say, the size of a MyClass object is 0x68, and I want an array of 0x20. So the total size is sizeof(MyClass) * 0x20 = 0xD00, or so I thought.
Now when I use my overloaded placement new[] operator :
pArr = new(pAllocatedMem)MyClass[0x20];
The compiler returned size_t to the new[] operator is actually 0xD08. There is an extra 8 bytes. Looking at the value of that 8 bytes, it's used to store the size of the array (0x20 in this case).
So is there a constant definition of what this header size is, say from WDK, that I can use? Does this size changes depends on compilers or what else?
That amount of that extra space, if any, is compiler dependent. For the languange definition [expr.new, paragraph 15]:
[It] is a non-negative unspecified value representing array allocation overhead; the result of the new-expression will be offset by this amount from the value returned by operator new[].
It is typically used by the runtime to know how many objects to destroy when delete is eventually called on the array.
Adding to the answer myself.
Did a little test with a colleague. It appears if the class to be initialized has a defined destructor function, the VS compiler will need that overhead storing the array size. No destructor, no overhead. Is that a bug or a feature?!.
#include <malloc.h>
#include <new>
class Foo
{
public:
//Foo() {}
//~Foo() {} // <-- will cause overhead even with user-allocated memory passed to placement new()
int a;
};
int main()
{
int n = 0x10;
size_t size = sizeof(int) * 2 + sizeof(Foo) * n;
void* p = malloc(size);
*((int*)p) = 0xaaaaaaaa;
*(int*)((char*)p + size - sizeof(int)) = 0xbbbbbbbb;
// Placement new with user-allocated memory
Foo* pf = new ((char*)p + sizeof(int)) Foo[n];
for (int i = 0; i < n; i++)
{
pf[i].a = i;
}
free(p);
return 0;
}

What does the C++ new operator do other than allocation and a ctor call?

What are all the other things the new operator does other than allocating memory and calling a constructor?
The C++ standard has this to say about the single object form (the form usually used) of the new operator from the <new> header:
Required behavior:
Return a nonnull pointer to suitably aligned storage (3.7.3), or else throw a
bad_alloc exception. This requirement is binding on a replacement version of this function.
Default behavior:
— Executes a loop: Within the loop, the function first attempts to allocate the requested storage. Whether
the attempt involves a call to the Standard C library function malloc is unspecified.
— Returns a pointer to the allocated storage if the attempt is successful. Otherwise, if the last argument to
set_new_handler() was a null pointer, throw bad_alloc.
— Otherwise, the function calls the current new_handler (18.4.2.2). If the called function returns, the loop
repeats.
— The loop terminates when an attempt to allocate the requested storage is successful or when a called
new_handler function does not return.
The standard has a lot of other stuff to say about the new operator and dynamic memory allocation (an awful lot to say), but I think the "Default behavior" list sums up the basics of the new operator pretty well.
I've written a explanation of what it does in this answer. It explains how
new gets the memory
new handles memory failure
new handles constructor exceptions
new handles special placement and nothrow versions
Michael explained how the default allocator function (::operator new) gets memory nicely and how it handles failure. I've seen your question on where the size of an object is stored in his comments. The answer is, there isn't size stored if not necassary. Remember that C doesn't need the size for free (and ::operator new can just use malloc):
void * memory = malloc(x);
free (memory); // no need to tell it the size
Here is an example where you see how storing the size has an impact on the size of allocation for the array form of a new expression (not covered by my other answer):
#include <cstddef>
#include <iostream>
struct f {
// requests allocation of t bytes
void * operator new[](std::size_t t) throw() {
void *p = ::operator new[](t);
std::cout << "new p: " << p << std::endl;
std::cout << "new size: " << t << std::endl;
return p;
}
// requests deleting of t bytes starting at p
void operator delete[](void *p, std::size_t t) throw() {
std::cout << "delete p: " << p << std::endl;
std::cout << "size : " << t << std::endl;
return ::operator delete[](p);
}
};
int main() {
std::cout << "sizeof f: " << sizeof (f) << std::endl;
f * f_ = new f[1];
std::cout << "&f_ : " << f_ << std::endl;
delete[] f_;
}
It will print out something like this:
sizeof f: 1
new p: 0x93fe008
new size: 5
&f_ : 0x93fe00c
delete p: 0x93fe008
size : 5
One byte for the object itself and 4 bytes for the count which is stored just before the allocated area of the object. Now if we use the deallocation function without a size parameter (just removing it from the operator delete), we get this output:
sizeof f: 1
new p: 0x9451008
new size: 1
&f_ : 0x9451008
delete p: 0x9451008
The C++ runtime here doesn't care about the size, so it doesn't store it anymore. Note that this is highly implementation specific, and that's what gcc does here to be able to tell you the size in the member operator delete. Other implementations may still store the size, and will most likely if there is a destructor to invoke for the class. For example just adding ~f() { } above makes gcc to store the size, regardless on what deallocation function we write.
Depends on if it's overloaded or not, if you built the app for debugging, if you're using a memory leak detector, if you have some kind of memory pooling scheme, if you have something like the Boehm garbage collector that's marking/unmarking bits, etc., etc. It could be doing a lot of custom stuff inside, or nothing special at all.