Chapter 3: Memory Management - C++ Memory Management

Series Article Directory



foreword

C++ has its own memory management.


C/C++ memory distribution

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
    
    
	static int staticVar = 1;
	int localVar = 1;
	int num1[10] = {
    
     1, 2, 3, 4 };
	char char2[] = "abcd";
	const char* pChar3 = "abcd";
	int* ptr1 = (int*)malloc(sizeof(int) * 4);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
	free(ptr1);
	free(ptr3);
}
1. 选择题:
选项 : A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?__C__ staticGlobalVar在哪里?__C__
staticVar在哪里?__C__ localVar在哪里?__A__
num1 在哪里?__A__

char2在哪里?__A__ * char2在哪里?__A__
pChar3在哪里?__A__ * pChar3在哪里?__D__
ptr1在哪里?__A__ * ptr1在哪里?__B__

2. 填空题:
sizeof(num1) = __40__;
sizeof(char2) = __5__; strlen(char2) = __4__;
sizeof(pChar3) = __4__; strlen(pChar3) = __4__;
sizeof(ptr1) = __4__;

3. sizeof 和 strlen 区别?

insert image description here

  1. The stack is also called the stack – non-static local variables/function parameters/return values, etc., the stack grows downward.

  2. Memory-mapped segments are efficient I/O mappings for loading a shared dynamic memory bank. Users can use the system interface
    to create shared shared memory for inter-process communication. (If you haven't learned this in the Linux course, you only need to understand it now)

  3. The heap is used for dynamic memory allocation when the program is running, and the heap can grow upwards.

  4. Data segment – ​​stores global and static data.

  5. Code Segment – ​​Executable code/read-only constants.

Divide the memory and manage data by category

Dynamic memory management in C language

void Test()
{
    
    
	int* p1 = (int*)malloc(sizeof(int));
	free(p1);
	// 1.malloc/calloc/realloc的区别是什么?
	int* p2 = (int*)calloc(4, sizeof(int));
	int* p3 = (int*)realloc(p2, sizeof(int) * 10);
	// 这里需要free(p2)吗?
	free(p3);
}

interview questions

  1. The difference between malloc/calloc/realloc?

  2. The realization principle of malloc?

C++ memory management mode

The C language memory management method can continue to be used in C++, but it is powerless in some places, and it is more troublesome to use, so C++ has proposed its own memory management method: dynamic memory management through new and delete operators .

new/delete operation built-in type

void Test()
{
    
    
	// 动态申请一个int类型的空间
	int* ptr4 = new int;
	// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);
	// 动态申请10个int类型的空间
	int* ptr6 = new int[3];
	delete ptr4;
	delete ptr5;
	delete[] ptr6;
}
int main()
{
    
    
	//int* p1 = new int;  // 不会初始化
	int* p1 = new int(10); // 申请一个int,初始化10
	int* p3 = new int[10]; // 申请10个int的数组
	int* p4 = new int[10] {
    
    1, 2, 3, 4};

	int* p2 = (int*)malloc(sizeof(int));
	if (p2 == nullptr)
	{
    
    
		perror("malloc fail");
	}

	return 0;
}

insert image description here

Note:
To apply for and release the space of a single element, use the new and delete operators, to apply for and release continuous space, use new[] and delete[], note: use them together.
The essence of new/delete is to open up space for custom types.

new and delete operations on custom types

class A
{
    
    
public:
	A(int a = 0)
		: _a(a)
	{
    
    
		cout << "A():" << this << endl;
	}
	~A()
	{
    
    
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
    
    
	// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数
	A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);
	free(p1);
	delete p2;
  
	// 内置类型是几乎是一样的
	int* p3 = (int*)malloc(sizeof(int)); // C
	int* p4 = new int;
	free(p3);
	delete p4;
  
	A* p5 = (A*)malloc(sizeof(A) * 10);
	A* p6 = new A[10];
	free(p5);
	delete[] p6;
  
	return 0;
}

Note: When applying for a custom type of space, new will call the constructor, delete will call the destructor, but malloc and free will not.

operator new and operator delete functions

new and delete are operators for users to apply and release dynamic memory . operator new and operator delete are global functions provided by the system . new calls the operator new global function at the bottom layer to apply for space, and delete uses the operator delete global function at the bottom layer to release space.

Implementation principle of new and delete

built-in type

If you apply for a built-in type of space, new and malloc, delete and free are basically similar, the difference is: new/delete applies for and releases the space of a single element, new[] and delete[] apply for continuous space, and new will throw an exception when the space application fails, and malloc will return NULL.

custom type

  • The principle of new
  1. Call the operator new function to apply for space
  2. Execute the constructor on the requested space to complete the construction of the object
  • The principle of delete
  1. Execute the destructor on the space to complete the cleanup of resources in the object
  2. Call the operator delete function to release the object's space
  • The principle of new T[N]
  1. Call the operator new[] function, and actually call the operator new function in operator new[] to complete the application of N object spaces
  2. Execute the constructor N times on the requested space
  • The principle of delete[]
  1. Execute N times of destructors on the released object space to complete the cleanup of resources in N objects
  2. Call operator delete[] to release space, actually call operator delete in operator delete[] to release space
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
    
    
	// try to allocate size bytes
	void* p;
	while ((p = malloc(size)) == 0)//用malloc开辟空间
		if (_callnewh(size) == 0)
		{
    
    
			// report no memory
			// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
			static const std::bad_alloc nomem;
			_RAISE(nomem);
		}
	return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
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));
	_free_dbg(pUserData, pHead->nBlockUse);
	__FINALLY
		_munlock(_HEAP_LOCK); /* release other threads */
	__END_TRY_FINALLY
		return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

The essence of new and delete is the package of malloc and free.
Through the implementation of the above two global functions, we know that operator new actually applies for space through malloc. If malloc successfully applies for space, it returns directly. Otherwise, it executes the countermeasures provided by the user for insufficient space. If the user provides this measure, it continues to apply, otherwise it throws an exception. operator delete finally releases space through free.

class A
{
    
    
public:
	A(int a = 0)
		: _a(a)
	{
    
    
		cout << "A():" << this << endl;
	}

	~A()
	{
    
    
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};


class Stack
{
    
    
public:
	Stack()
	{
    
    
		cout << "Stack()" << endl;
		_a = new int[4];
		_top = 0;
		_capacity = 4;
	}

	~Stack()
	{
    
    
		cout << "~Stack()" << endl;
		delete[] _a;
		_top = _capacity = 0;
	}

private:
	int* _a;
	int _top;
	int _capacity;
};


int main()
{
    
    
	// 失败了抛异常
	//int* p1 = (int*)operator new(sizeof(int*));

	 失败返回nullptr
	//int* p2 = (int*)malloc(sizeof(int*));
	//if (p2 == nullptr)
	//{
    
    
	//	perror("malloc fail");
	//}

	// 申请空间 operator new -> 封装malloc
	// 调用构造函数
	A* p5 = new A;

	// 先调用析构函数
	// 再operator delete p5指向的空间
	// operator delete -> free
	delete p5;

	// 申请空间 operator new[] ->perator new-> 封装malloc
	// 调用10次构造函数
	A* p6 = new A[10];
	
	// 先调用10次析构函数
	// 再operator delete[] p6指向的空间
	delete[] p6;


	int* p7 = new int[10];
	free(p7);  // 正常释放

	A* p8 = new A;
	//free(p8); // 少调用的析构函数
	delete p8;

	Stack st;

	Stack* pst = new Stack;
	//delete pst;
	free(pst); // 少调用了析构函数 -- 有内存泄漏

	// 结论:new/malloc系列 有底层实现机制有关联交叉。
	// 不匹配使用可能有问题,可能没问题,建议大家一定匹配使用

	A* p9 = new A[10];
	//free(p9);
	//delete p9;
	delete[] p9;

	return 0;
}

insert image description here
insert image description here

Conclusion:
The underlying implementation mechanisms of the new/malloc series are related to each other.
There may be problems if they do not match, but it may not be a problem. It is recommended that you use them in a matching manner.
new[] stores the number of times the destructor needs to be called in an integer space before releasing the space, and delete[] will get the number of times and call the destructor, while free/detele will not. But when the destructor does not need to be called, no more integer space will be opened.

Position new expression (placement-new)

Positioning the new expression is to call the constructor to initialize an object in the allocated original memory space.

Format:
new (place_address) type or new (place_address) type(initializer-list)
place_address must be a pointer, and initializer-list is the initialization list of the type

Usage scenario:
positioning new expressions are generally used in conjunction with memory pools in practice. Because the memory allocated by the memory pool is not initialized, if it is an object of a custom type, it needs to use the definition expression of new to explicitly call the constructor for initialization.

class A
{
    
    
public:
	A(int a = 0)
		: _a(a)
	{
    
    
		cout << "A():" << this << endl;
	}

	~A()
	{
    
    
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};


int main()
{
    
    
	A aa;

	A* p1 = (A*)malloc(sizeof(A));
	if (p1 == nullptr)
	{
    
    
		perror("malloc fail");
	}

	// 对一块已有的空间初始化 -- 定义new
	//new(p1)A;
	new(p1)A(1);

	A* p2 = new A;

	p1->~A();
	free(p1);

	delete p2;


	return 0;
}

insert image description here

common interview questions

The difference between malloc/free and new/delete

What malloc/free and new/delete have in common is that they all apply for space from the heap and need to be released manually by the user. The differences are:

  1. malloc and free are functions, new and delete are operators

  2. The space requested by malloc will not be initialized, but new can be initialized

  3. When malloc applies for space, you need to manually calculate the size of the space and pass it on. New just needs to follow it with the type of space. If there are multiple objects, specify the number of objects in []

  4. The return value of malloc is void*, which must be forced when used, and new does not need it, because new is followed by the type of space

  5. When malloc fails to apply for space, it returns NULL, so it must be judged as empty when using it, new does not need it, but new needs to catch exceptions

  6. When applying for a custom type of object, malloc/free will only open up space, and will not call the constructor and destructor, while new will call the constructor to complete the initialization of the object after applying for space, and delete will call the destructor to clean up the resources in the space before releasing the space

Usage + underlying principle

memory leak

What is a memory leak, the harm of memory leaks

What is a memory leak: A memory leak refers to a situation in which a program fails to release memory that is no longer in use due to negligence or error. A memory leak does not refer to the physical disappearance of memory, but rather the loss of control over a certain segment of memory due to a design error after the application allocates a certain segment of memory, resulting in a waste of memory.
Hazards of memory leaks: Memory leaks occur in long-running programs, which have a great impact, such as operating systems, background services, etc. Memory leaks will lead to slower and slower responses, and eventually freeze.

void MemoryLeaks()
{
    
    
	// 1.内存申请了忘记释放
	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = new int;
	// 2.异常安全问题
	int* p3 = new int[10];
	Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
	delete[] p3;
}

Classification of memory leaks

In C/C++ programs, we generally care about two aspects of memory leaks:

  • Heap memory leak (Heap leak)

Heap memory refers to a piece of memory allocated from the heap through malloc / calloc / realloc / new, etc. as needed during program execution. After use, it must be deleted by calling the corresponding free or delete. Assuming that the design error of the program causes this part of the memory to not be released, then this part of the space will no longer be used in the future, and Heap Leak will occur.

  • system resource leak

Refers to the resources allocated by the system used by the program, such as sockets, file descriptors, pipes, etc., which are not released using the corresponding functions, resulting in a waste of system resources, which can seriously lead to reduced system performance and unstable system execution.

How to detect memory leaks

Under VS, you can use the _CrtDumpMemoryLeaks() function provided by the Windows operating system for simple detection. This function only reports the approximate number of bytes leaked, and there is no other more accurate location information.

int main()
{
    
    
	int* p = new int[10];
	// 将该函数放在main函数之后,每次程序退出的时候就会检测是否存在内存泄漏
	_CrtDumpMemoryLeaks();
	return 0;
}

// 程序退出后,在输出窗口中可以检测到泄漏了多少字节,但是没有具体的位置
Detected memory leaks!
Dumping objects ->
{
    
    79} normal block at 0x00EC5FB8, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

Therefore, you must be careful when writing code, especially when operating dynamic memory, you must remember to release it. However, in some cases, it is always impossible to prevent, and the simple method can be used to quickly locate it. If the project is relatively large and there are many memory leak locations, it is usually handled with the help of third-party memory leak detection tools when it is not easy to check.

How to avoid memory leaks

  1. Good design specifications in the early stage of the project, develop good coding standards, and remember to release the memory space that matches. ps: This ideal state. But if you encounter an exception, even if you pay attention to release, there may still be problems. It needs to be managed by the next smart pointer to be guaranteed.

  2. Use RAII ideas or smart pointers to manage resources.

  3. Some company internal specifications use internally implemented private memory management libraries. This library comes with options for memory leak detection.

  4. Something went wrong using a memory leak tool to detect. ps: However, many tools are not reliable enough, or the fees are expensive.

in conclusion:

Memory leaks are very common, and there are two solutions:

  1. preventive type. Such as smart pointers, etc.

  2. After the fact, check the error type. Such as leak detection tools.

Summarize

C++ has its own set of memory management methods.
Life is a never-ending, restless, merciless battle, and anyone who wants to be worthy of being called a human must constantly fight against an invisible enemy. — Romain Rolland

Guess you like

Origin blog.csdn.net/yanyongfu523/article/details/131716609