new and delete of C + + memory allocation

[TOC]

Creating and destroying objects in heap memory in C + + requires the keywords new and delete. New and delete are not only keywords in C + +, but also a special operator.

memory allocation

There are many ways to allocate and release memory in C + +

distributionreleaseGenusCan I reload
malloc()free()C functionmust not
newdeleteC + + expressionmust not
::operator new()::operator delete()C + + functionscan
allocator::allocate()allocator::deallocate()C + + standard libraryIt can be designed freely and matched with the container

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-xQpssnQf-1631423431554)(E:\xugd\Ebooks\FangCloudV2 \ personal files \ EBooks - technology programming language \ Blog(Markdown)]\pictures\C++\C + + memory allocation call level. png)

The following example of GUN:

void* p1=malloc(512);
free(p1);

Complex<int>* p2=new Complex<int>; // one object
delete p2;

void *p3=::operator new(512);  // 512 byte
::operator delete(p3);

// gun2.9, static function
// void* p4=alloc::allocate(512);
// alloc::deallocate(p4,512);

// gun4.9; Assign 9 int s
void* p4=allocactor<int>().allocate(9);
allocactor<int>().deallocate((int*)p4,9);

// alloc::allocate equivalent to 2.9; But they all have to be called through objects
void* p5=__gnu_cxx::__pool_alloc<int>().allocate(9);
__gnu_cxx::__pool_alloc<int>().deallocate((int*)p5, 9);

Distribution approach

When C + + allocates a new object, there will be a fixed call path. Some functions can be overloaded to modify the memory allocation and release methods:

[external link picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-37ybbUVD-1631423431560)(E:\xugd\Ebooks\FangCloudV2 \ personal files \ EBooks - technology programming language \ Blog(Markdown)]\pictures\C++\C + + memory allocation path. png)

new can have multiple overloaded versions:

  • Only size_t the of a parameter is standard class operator new: after overloading, the default new object will eventually call this function to allocate memory;
  • The two parameters (size_t + void *) are standard placement new: after overloading, objects can be built on existing memory;
class Bar{
public:
    Bar(){
        cout<<"Bar::Bar()"<<endl;
    }
    Bar(int){
        cout<<"Bar::Bar(int)"<<endl;
        throw "Error";
    }
    ~Bar(){
        cout<<"Bar::~Bar()"<<endl;
    }

    // General operator new overload
    void* operator new(std::size_t size){
        cout<<"operator new(std::size_t)"<<endl;
        return malloc(size);
    }
    void operator delete(void*, std::size_t){
        cout<<"operator delete(void*, std::size_t)"<<endl;
    }

    // Standard placement new overload
    void* operator new(std::size_t size, void* start){
        cout<<"placement new(std::size_t, start)"<<endl;
        return start;
    }
    void operator delete(void*, void*){
        cout<<"operator delete(void*, void*)"<<endl;
    }

    // New (custom) placement new overload
    void* operator new(std::size_t size, long extra){
        cout<<"placement new(std::size_t, extra)"<<endl;
        return malloc(size+extra);
    }
    void operator delete(void*, long){
        cout<<"operator delete(void*, long)"<<endl;
    }

    void* operator new(std::size_t size, long extra, char init){
        cout<<"placement new(std::size_t, extra, init)"<<endl;
        return malloc(size+extra);
    }
    void operator delete(void*, long, char){
        cout<<"operator delete(void*, long, char)"<<endl;
    }
};

void testBarAlloc() {
    Bar *p1 = new Bar();
//    Bar *p2 = new Bar[3]; //  Because the operator new [] is not overloaded, the system default will be used
    char *buf = new char[sizeof(Bar)];
    Bar *p3 = new(buf)Bar();
    Bar *p4 = new(2)Bar();
    Bar *p5 = new(2, 'a')Bar();
//    Bar *p6 = new(buf)Bar(1); //  Will terminate directly
    cout << "p1=" << p1 << ", p3=" << p3 << ", p4=" << p4 << ", p5=" << p5 << endl;

    // All deletes will call operator delete; placement delete will not be called
    delete p5;
    delete p4;
    delete p3;
    delete p1;
}

// operator new(std::size_t)
// Bar::Bar()
// placement new(std::size_t, start)
// Bar::Bar()
// placement new(std::size_t, extra)
// Bar::Bar()
// placement new(std::size_t, extra, init)
// Bar::Bar()
// p1=0x7fffba5cc8c0, p3=0x7fffba5cc8e0, p4=0x7fffba5cc900, p5=0x7fffba5cc920
// 
// Bar::~Bar()
// operator delete(void*, std::size_t)
// Bar::~Bar()
// operator delete(void*, std::size_t)
// Bar::~Bar()
// operator delete(void*, std::size_t)
// Bar::~Bar()
// operator delete(void*, std::size_t)

array new

Array needs to allocate and release memory through new [] and delete [] (array new and delete will be called);

class Foo{
public:
    Foo():id(0){
        cout<<"default ctor.this="<<this<<", id="<<id<<endl;
    }
    Foo(int n):id(n){
        cout<<"ctor.this="<<this<<", id="<<id<<endl;
    }
    ~Foo(){
        cout<<"dtor.this="<<this<<", id="<<id<<endl;
    }

private:
    int id;
};

void testArrayFooAlloc(){
    Foo* buf = new Foo[3]; // 3-time default ctor

    Foo* pTmp = buf;
    cout<<"buf="<<buf<<", tmp="<<pTmp<<endl;
    for(int i=0; i<3; ++i){
        new(pTmp++)Foo(i+1); // Call the constructor through placement new
    }
    cout<<"buf="<<buf<<", tmp="<<pTmp<<endl;

    delete[] buf; // 3-time dtor, in reverse order; If delete, dtor will be called only once
}

// default ctor.this=0x7fffd91758c8, id=0
// default ctor.this=0x7fffd91758cc, id=0
// default ctor.this=0x7fffd91758d0, id=0
// buf=0x7fffd91758c8, tmp=0x7fffd91758c8
// 
// ctor.this=0x7fffd91758c8, id=1
// ctor.this=0x7fffd91758cc, id=2
// ctor.this=0x7fffd91758d0, id=3
// buf=0x7fffd91758c8, tmp=0x7fffd91758d4
// 
// dtor.this=0x7fffd91758d0, id=3
// dtor.this=0x7fffd91758cc, id=2
// dtor.this=0x7fffd91758c8, id=1

The memory allocated through new [] will add information about the number of objects in front of the object (its header includes: Cookie + [debug header] + object count). Take the Demo class containing three int data as an example:

  • What is returned to the customer is the point to the beginning of the data (00481c34);
  • When the customer uses delete [] to delete, it will be adjusted to (00481c30), so as to obtain the number of arrays and call the destructor of the corresponding number.

[external chain picture transfer failed, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-9Qnwz2n5-1631423431563)(E:\xugd\Ebooks\FangCloudV2 \ personal files \ EBooks - technology programming language \ Blog(Markdown)]\pictures\C++\array_new memory allocation structure. png)

The size of the memory block is stored in the Cookie (the lowest bit is used to indicate whether it has been allocated: 1 ID has been allocated, 0 ID has been recycled). The composition of the above 0x60 is:

  • 32 + 4: debug header information (see 3.2 for details)‘ debug header ’Part);
  • 4: Is the next gap of debug information;
  • 36: requested memory size (sizeof(Demo)*3);
  • 4 * 2: upper and lower Cookie size;
  • Fill to 16 times and add 12 more fills (84 - > 96);

operator new

Both global and operator new in the class can be overloaded; Class, whether static is added or not, operator new will be used as a static member.

Constructors cannot be called directly (by the compiler or through placement new), while destructors can be called directly.

placement new

operator new with parameters is placement new; Placement new allows object s to be constructed in allocated memory.

You can overload class member operator new() and write out multiple versions, as long as the parameters are different:

  • The first parameter must be size_t: Is the memory size;
  • When the second is a pointer, it is a standard placement new: except for the first parameter, other parameters are passed as new(...) parameters;

You can also overload class member operator delete() and write out multiple versions, but they will never be called by delete by default. These overloaded versions are called only when new calls ctor and throws an exception.

char* buf = new char[sizeof(Complex)];
Complex* pc = new(buf)Complex(1,2); // Call the constructor on the first element
// ...
delete[] buf;

The above second line of code (placement new) will be converted to the following similar code (compiler):

Complex* pc;
try{
    void* mem=operator new(sizeof(Complex));
    pc=static_cast<Complex*>(mem);
    pc->Complex::Complex(1,2);
}
catch(std::bad_alloc){
    // allocation failed, unknown Constructor
}

new handler

STD:: bad is thrown when operator new cannot allocate the required memory_ Alloc abnormality; A handler will be called (more than once) before throwing an exception:

typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();

A well-designed new handler has only two options:

  • Release the memory and make the memory allocation successful;
  • Call abort() or exit();

If you want new to return 0 instead of throwing an exception when the memory application fails, you need to use the nothrow version:

new (nothrow) Foo

allocator

The allocator is implemented in gun2.9; But in gun4.9, the default allocator is just a simple call to new. To use an allocator similar to the allocator in 2.9 (apply for memory in batches and then divide it for each element), you need to use__ gnu_cxx::__pool_alloc:

vector<int, __gnu_cxx::__pool_alloc<int>> vecPool;

Some memory allocation allocators built in gun C + +

  • __ gnu_cxx::new_allocator: directly call operator new/delete;
  • __ gnu_cxx::malloc_allocator directly calls malloc/free;
  • __ gnu_cxx::bitmap_allocator: use bit map to track used and unused memory blocks;
  • __ gnu_cxx::pool_allocator: cache pool mode;
  • __ gnu_cxx::__mt_alloc: multithreaded version;
  • __ gnu_cxx::debug_allocator: a wrapper;
  • __ gnu_cxx::array_allocator: allocate a known and fixed size memory block (STD:: array);

In gun2.9, the memory in the size range of (8 * 1 ~ 8 * 16) will be managed in pool mode; When memory is needed, a large area will be allocated, and then connected in series in the form of linked list; Directly from free when necessary_ List; When the object is released, it is returned to free_list (but not returned to the operating system).

[the external chain picture transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-GPeqVasg-1631423431566)(E:\xugd\Ebooks\FangCloudV2 \ personal files \ EBooks - technology programming language \ Blog(Markdown)]\pictures\C++\gun2.9_allocator-1.png)

allocate process (set n as the requested memory size and MAX as the maximum memory allowed in the linked list (128)):

  • If n > max, directly apply for memory from the system (that is, finally call malloc);
  • Find n the corresponding index in the linked list and check whether there are available empty nodes;
  • If the memory allocation for the node fails (rarely occurs), try to find out whether there is an empty node on the right (a node larger than the current node); If yes, it is borrowed (mounted under the current node after splitting).

Apply for memory from the system: each time memory is allocated, in addition to the required memory, an additional part will be allocated (size * 20 * 2 + round_up (total > > 4). Total is the cumulative amount of allocated memory) and reserved for subsequent use;

  • Blocks of integer multiples of the current size (generally size*20): one block is allocated (returned to the customer), and the rest are connected together with a linked list;
  • Reserved block: (i.e. the block pointed by start_free and end_free)
 // __ALIGN=8
size_t ROUND_UP(size_t bytes){ // Aligned memory (must be a multiple of 8)
    return (((bytes) + __ALIGN-1) & ~(__ALIGN-1));
}

size_t FREELIST_INDEX(size_t bytes){ // Get the index in the linked list
    return (((bytes) + __ALIGN-1)/__ALIGN - 1);
}

malloc memory layout

malloc is not slow to allocate memory in the operating system (various optimizations have been made), but it will increase cookies and various headers, so it will waste memory (additional headers and data filled for alignment).

VC6 is used to illustrate the memory layout of malloc.

struct page

When malloc requests memory, it will add cookie s for subsequent free and verification:

  • Cookie is the size of the allocated memory (including debug header information): the lowest bit is set to 1 during allocation;
  • When releasing, the lowest bit of the Cookie is changed back to 0: then you can judge whether the upper and lower adjacent blocks can be merged according to the Cookie.

[external chain picture transfer failed, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-etkBy5DM-1631423431567)(E:\xugd\Ebooks\FangCloudV2 \ personal files \ EBooks - technology programming language \ blog (markdown)] \ pictures \ C + + \ VC malloc page. PNG)

Each time 1M is applied from the operating system, it is divided into 32 groups (32K size), and each group is divided into eight pages (4K), that is, the left part of the above figure (its available memory is 4K-4*2-8=4080); All memory requests less than 1016(1k-2*4) are applied from the page (aligned to a multiple of 16).

debug header

To facilitate debugging, the debug header information will be added in the debug mode:

  • Pointer used to connect the allocated memory in a linked list (Next and Prev);
  • szFileName and nLine indicate the location of the requested memory (call malloc) code;
  • lRequest: allocation ID (if the memory allocated for the first block above is 1);
  • gap: it is a protected area (the allocated area is set to 0xFD, the recycled area is set to 0xDD, and the cleaned area is set to 0xCD);

[external chain picture transfer fails, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-4tV0kqNy-1631423431569)(E:\xugd\Ebooks\FangCloudV2 \ personal files \ EBooks - technology programming language \ blog (markdown)] \ pictures \ C + + \ VC6 malloc debug header. png)

VC program call stack

The following is the call stack when VC6 program starts, which is in the new version (VC10 and later)__ sbh * * functions no longer exist (directly call the memory allocation of the system).

[external chain picture transfer fails, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-Di0qZ5BM-1631423431571)(E:\xugd\Ebooks\FangCloudV2 \ personal files \ EBooks - technology programming language \ Blog(Markdown)]\pictures\C++\VC6 - program startup call level. png)

sbh: Small block heap, which is used to optimize and manage small memory allocation; It is no longer needed in the new version (the operating system has been optimized accordingly).

Keywords: C++ new malloc delete

Added by TheUnknown on Fri, 19 Nov 2021 23:23:14 +0200