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.
Related
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
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.
I'd like to implement something like this, where native memory is allocated on the Java heap. In order for this to work, I need this subset of the code to work:
const static int pad = 16;
void * operator new(size_t t) throw(std::bad_alloc) {
void* padded = malloc((int) t + pad);
void* p = static_cast<void *>(static_cast<char *>(padded) + pad);
std::cout << "new: " << p << " padded " << padded << " size: " << t << std::endl;
return p;
}
void operator delete(void *p) throw() {
if (p != 0) {
void *padded = static_cast<void *>(static_cast<char *>(p) - pad);
std::cout << "del: " << p << " padded " << padded << std::endl;
free(padded);
} else {
std::cout << "pointer is zero!" << std::endl;
}
}
Where what I'm trying to do is to add some bytes to every memory allocation. This seems to work ok (most deletes are successful), but I'm getting an error:
java(379,0x700002206000) malloc: *** error for object 0x7fc104122180: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Where the stack trace indicated that free was called immediately (i.e., not my delete) from this line:
name = temp.str();
Where name is a string, and temp is a stringstream. I tried reproducing this error in a non-JNI context, but it didn't crash there.
If I'm padding the memory like this, then clearly it would be wrong to free something that had been allocated via new or to delete something that had been allocated directly from malloc. Is that even possible though?
A search for the address in the error in my stdout supports this:
new: 0x7fc104122190 padded 0x7fc104122180 size: 32
del: 0x7fc104122190 padded 0x7fc104122180
new: 0x7fc104122180 padded 0x7fc104122170 size: 32
where it looks like the address was successfully allocated and then deallocated. The same address appeared again as the unpadded address in the last line printed to stdout (I think it's a coincidence that this address ended up as the unpadded address in the 2nd allocation). But since this is the last line printed to std out, the failed attempt to free it is not passing through code I'm seeing (even if I override free itself).
Is it possible to get this approach to work? Or must I narrow the scope of my new/delete to objects that I fully manage myself?
Besides new() and delete(), there are new[]() and delete[]() operators. There are some inconsistencies in the standard library when, say, new()[] operator is used to allocate an array and then delete() used to deallocate it. If you haven't overloaded all four operators, these inconsistency may cause memory allocation errors. Note that in C++17 it will be even more new/delete operators.
I'm reading "Thinking in C++" and I'm confused by the new operator. Here is the code from the book:
//: C13:ArrayOperatorNew.cpp
// Operator new for arrays
#include <new> // Size_t definition
#include <fstream>
using namespace std;
ofstream trace("ArrayOperatorNew.out");
class Widget
{
enum { sz = 10 };
int i[sz];
public:
Widget() { trace << "*"; }
~Widget() { trace << "~"; }
void* operator new(size_t sz)
{
trace << "Widget::new: "
<< sz << " bytes" << endl;
return ::new char[sz];
}
void operator delete(void* p)
{
trace << "Widget::delete" << endl;
::delete []p;
}
void* operator new[](size_t sz)
{
trace << "Widget::new[]: "
<< sz << " bytes" << endl;
return ::new char[sz];
}
void operator delete[](void* p)
{
trace << "Widget::delete[]" << endl;
::delete []p;
}
};
int main()
{
trace << "new Widget" << endl;
Widget* w = new Widget;
trace << "\ndelete Widget" << endl;
delete w;
trace << "\nnew Widget[25]" << endl;
Widget* wa = new Widget[25];
trace << "\ndelete []Widget" << endl;
delete []wa;
} ///:~
and here is the content of the trace in "ArrayOperatorNew.out"
new Widget
Widget::new: 40 bytes
*
delete Widget
~Widget::delete
new Widget[25]
Widget::new[]: 1004 bytes
*************************
delete []Widget
~~~~~~~~~~~~~~~~~~~~~~~~~
Widget::delete[]
I'm confused about the number 1004. Why it's not 1000? The book says:
This extra four bytes is where the
system keeps information about the
array, in particular, the number of
objects in the array.
But what's the system? How is this accomplished? Compiler helps here?
When using new[] the runtime needs some way to remember the size of the array allocated, so it knows how much to deallocate when using delete[]. In your particular implementation it's way of remembering is allocating the extra four bytes which hold the size (it doesn't have to work this way).
You can read more about this in the C++ FAQ.
This FAQ After p = new Fred[n], how does the compiler know there are n objects to be destructed during delete[] p? answers exactly this question.
That's a compiler-dependent detail.
When delete[] is invoked it is passed only one parameter - the pointer to the array. In order to run correctly it has to know the number of elements to execute the destructors on exactly the right number of objects. So it has to get that information somewhere.
The typical approach is that new[] prepends the array payload with an extra size_t that stores the number of elements. Thus the amount of space allocated will be
sizeof( size_t ) + numberOfElements * sizeof ( ObjectType )
When allocating an array with new then, an additional word is used in the beginning of the allocated block to keep the number of allocated bytes.
As the C++ array do not keep information about their size, the memory manager must keep tabs over the size of allocated memory, when delete[] is used the number of allocated byes is read and then the memory manager fees that amount of memory.
that is the reason why calling delete[] for a single variable can be disastrous
and calling delete for an array causes a memory leak.
The memory management has to keep some information about the size of the memory block. Without that information, delete / delete[] can not work correctly (in case of delete, this information may not be necessary sinc the compiler knows the size of object being deleted).
How the information is kept and where is an implementation detail of new/delete. It may change depending on the compiler or the memory management library you use (e.g. SmartHeap).
Sometimes additional memory is allocated to detect programming errors (like writing over the boundary of allocated memory).
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.