C + + Beginner Level - memory management

Introduction (Part C review)

Kernel space:
It belongs to the category of operating system
Stack: (down)
The function call establishes the stack frame. The parameters and local variables in the function are all stored in the stack frame
Heap: (up)
Theoretically, the memory address of the latter malloc is larger than that of the first malloc, but not necessarily, because the space requested next time may be released before
Data segment:
It is usually used to store initialized (non-0) global variables and static local variables in the program. The starting position of the data segment is confirmed by the link location file, and the size is automatically allocated when compiling the link. The data segment belongs to static memory allocation
Code snippet:
Code snippets are mapped in memory as read-only. It is calculated automatically by the compiler when compiling links. It is usually used to store executable code (binary instructions) and read-only constants. Code segments are input into static memory allocation

For the division of this area in the figure:

Take 32-bit 4G as an example

  1. If the kernel occupies 1G, most of the remaining 3G is heap
  2. The stack is very small. Generally, it is only 8M under linux, so stack overflow will occur when recursive calls are too deep
  3. The data and code segments are not very large

be careful:
Char in the figure above_ The V character array is stored on the stack, char_ V is the first element address dereference, and 'a' is also on the stack (pay attention to distinguish between ptr_char_v and * char_v)

malloc/calloc/realloc differences:
calloc is equivalent to malloc+memset(0) + initialization
realloc is to expand the space of malloc or calloc

C + + memory management

C language memory management can continue to be used in C + +, but it is powerless and troublesome in some places. Therefore, C + + puts forward its own memory management method: dynamic memory management through new and delete operators

1)new delete

new delete for built-in types

Implementation principle:
If a built-in space is applied for, new and malloc, delete and free are basically similar. The difference is that new/delete applies for and releases the space of a single element, new [] and delete [] apply for continuous space, and new throws an exception when the space application fails, and malloc returns NULL
Usage:

void test()
{
	// Dynamically apply for a space of type int
	int* ptr4 = new int;
	// Dynamically apply for a space of type int and initialize it to 10
	int* ptr5 = new int(10);
	// Dynamically apply for 10 int type spaces
	int* ptr6 = new int[3];
	delete ptr4;
	delete ptr5;
	delete[] ptr6;
}

Compare malloc/free
(malloc / free is no different from the built-in type new / delete)

new delete for custom types

Implementation principle:

  1. New: call the operator new function to apply for space, execute the constructor on the applied space, and complete the construction of the object
    Delete: execute the destructor on the space to clean up the resources in the object, and call the operator delete function to release the space of the object
  2. New []: call the operator new [] function, actually call the operator new function in operator new [] to complete the application of N object spaces, and execute the constructor n times on the applied space
    Delete[]: executes the N destructor on the released object space, completes the cleaning of resources in the N objects, calls operator delete[] to release the space, and actually invokes operator delete in operator delete[] to release the space.

Usage:

class Test
{
public:
	Test()
		: _data(0)
	{
		cout << "Test():" << this << endl;
	}
	~Test()
	{
		cout << "~Test():" << this << endl;
	}
private:
	int _data;
};
//new/delete for custom
void Test2()
{
	// Request an object of a single Test type
	Test* p1 = new Test;
	delete p1;
	// Apply for 10 Test type objects
	Test* p2 = new Test[10];
	delete[] p2;
}

Comparison with malloc/free

  1. new/delete will call the constructor and destructor of the custom type
  2. malloc/free does not call
    You can see that there are only 11 pairs of constructors and destructors called by new/delete

be careful:

  1. Be sure to match (malloc corresponds to free, new corresponds to delete, and new [] corresponds to delete []), otherwise it may crash (the test shows that the built-in type will not crash, while the user-defined type will crash)

  2. In C + +, it is recommended to use new/delete as much as possible

2) operator new and operator delete functions

malloc/free and new/delete processing methods for failed space applications

Introduction: malloc/free and new/delete handle space application failures differently:

For malloc/free:

// malloc failed and returned NULL
char* p1 = (char*)malloc((size_t)2*1024*1024*1024);
if (p1 == NULL)
{
	printf("malloc fail\n");
}
else
{
	printf("malloc success:%p\n", p1);
}

For new/delete:

// Unlike malloc, new failed to apply for space and threw exceptions
try
{
	char* p2 = new char[0x7fffffff]; // Error, throw an exception and jump to the position where the exception is caught
	printf("xxxx");//Subsequent statements will not be executed
}
catch (const exception& e)
{
	cout << e.what() << endl;
}

operator new

The source code is as follows:

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
      {       // try to allocate size bytes
       void *p;
       while ((p = malloc(size)) == 0)
               if (_callnewh(size) == 0)
               {       // report no memory
              		   //Memory request failed, throw bad_alloc exception
               static const std::bad_alloc nomem;
               _RAISE(nomem);
               }
       return (p);
       }

It can be considered that the operator new function encapsulates malloc and adds throwing exceptions

be careful:

  1. This is not an operator overload
  2. If it is not new, the constructor (new=operator new + constructor) will not be called
operator delete

The source code is as follows:

void operator delete(void *pUserData)
	{
	 	_CrtMemBlockHeader * pHead;
	 	RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
		if (pUserData == NULL)
			return;
	 	_mlock(_HEAP_LOCK); /* block other threads */
	 	__TRY
	 		/* get a pointer to memory block header */
	 		pHead = pHdr(pUserData);
	 		/* verify block type */
	 		_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
			//Call as free_ free_dbg
	 		_free_dbg( pUserData, pHead->nBlockUse );
	 	__FINALLY
	 		_munlock(_HEAP_LOCK); /* release other threads */
	 	__END_TRY_FINALLY
	 	return;
	 }

The DEBUG version of CRT defines a set of DEBUG version memory allocation functions (such as _malloc_dbg). After you include CRTDBG.h, if you are currently a DEBUG project, nine times out of ten there will be a _debugmacro. We find CRTDBG.h

It is found that free also calls _free_dbg
be careful:

  1. This function finally frees up space through free
  2. Not delete, destructor will not be called (delete = destructor + operator delete)
Class specific overloading of operator new and operator delete

Introduction: write a program to detect whether a ListNode has not been released and count
Some codes (overloaded part) are as follows:

	A(int a = 0)
	{
		cout << "A()" << this << endl;
	}
	void* operator new(size_t n)
	{
		_count++;
		return ::operator new(n);
	}
	void operator delete(void* p)
	{
		_count--;
		return ::operator delete(p);
	}
private:
	static int _count;
};
int A:: _count = 0;//static initialization

As mentioned earlier, new/delete is both a keyword and an operator. After overloading new and delete, it is equivalent to calling the operator overload when using new/delete. It is equivalent to calling the operator new/operator delete function, adding the counting operation, and finally printing _count

3) new/delete is analyzed in combination with the actual situation

class Stack
{
public:
	Stack(int capacity = 4)
		:_a(new int[capacity])
		, _size(0)
		, _capacity(capacity)
	{
		cout << "Stack(int capacity = 4)" << endl;
	}
	~Stack()
	{
		delete[] _a;
		_size = _capacity = 0;
		cout << "~Stack()" << endl;
	}
private:
	int* _a;
	int _size;
	int _capacity;
};
int main()
{
	Stack st;//Statement 1
	Stack* ps = new Stack;//Statement 2
	delete ps;
	return 0;
}

Parse Statements 1, 2

analysis:

  1. Statement 1: Stack st, instantiate an object st in the main function stack frame. When calling its constructor to initialize the list, new int[capacity] (new calls operator new, operator new calls malloc) will open up a space of capacity int on the heap. When out of scope, automatically call the destructor to delete [] in the function_ A will destroy the space on the heap (the so-called clean up resources). When the main function is given, ST will also be destroyed
  2. Statement 2: Stack* ps = new Stack; Define a 4-byte pointer ps on the main function Stack frame, and new a 12 byte Stack class (because it is new, it will open up the 12 byte space on the heap). At the same time, call its constructor, and new int[capacity] open up the space of capacity int on the heap to_ a. Return to ps pointer. When we want to release space, if we use free, only the space on the Stack will be released, so we can release it correctly by using delete. First call the destructor and then call operator delete to release space

4) Positioning new expression placement new

Concept: the positioning new expression is to initialize an object in the allocated original memory space by calling the constructor.

use:

new (place_address) class
new (place_address) class(initializer-list)
//place_address: a pointer
//Initializer list: initializer list of types

Scenario example:
First look at the following codes:

A a[5];
A* pb1 = new A[5];// Copy an array of a to another space b
for (int i = 0; i < 5; ++i)
{
	pb1[i] = a[i];
}

// Direct copy construction
A* pb2 = (A*)malloc(sizeof(A) * 5);
for (int i = 0; i < 5; ++i)
{
	new(pb2 + i)A(a[i]);
}

5) Summarize the role of malloc/free new/delete

  1. malloc and free are functions, and new and delete are operators
  2. When malloc applies for space, it needs to manually calculate the space size and pass it. new only needs to follow the space type
  3. The return value of malloc is void *, which must be forcibly converted when used. New is not required, because new is followed by the type of space
  4. When malloc fails to apply for space, it returns NULL, so it must be NULL when using it. New does not need it, but new needs to catch exceptions
  5. When applying for custom type objects, malloc/free will only open up space and will not call constructors and destructors, while new will apply for space
    After that, the constructor will be called to initialize the object, and delete will call the destructor to clean up the resources in the space before releasing the space

6) Memory leak

Keywords: C++ Back-end

Added by mebar3 on Sat, 23 Oct 2021 09:08:49 +0300