[C++] Memory management

Table of contents

Memory management::

                        1. C/C++ memory distribution

                        2. Dynamic memory management in C language

                        3. Dynamic memory management in C++

                        4. operator new and operator delete functions

                        5. Implementation principle of new and delete

                        6. Locating the new expression

                        7. Memory leaks

                        


Memory management::

1. 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 };
    //注:是将常量区的"abcd"拷贝到char2数组
	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/8;     strlen(pChar3) = 4;
sizeof(ptr1) = 4/8;

illustrate:

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

2. The memory-mapped segment is an efficient I/O mapping method for loading a shared dynamic memory bank. Users can use the system interface to create shared shared memory for inter-process communication.

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

4. Data segment, storing global data and static data

5. Code segment, which stores executable code or read-only constants

2. Dynamic memory management in C language: malloc/calloc/realloc/free

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:

The difference between malloc/calloc/realloc? (For details, see the blog [C language] dynamic memory management and flexible array)

3. Dynamic memory management in C++

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. Therefore, C++ has proposed its own memory management method, which uses new and delete operators for dynamic memory management.

new/delete operation built-in types

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;
}

 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[], pay attention, use them together.

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;
}

1. Use delete to release the space requested by new[]

1. Built-in types

There is no problem with these operations on built-in types such as int, char, float, and double.

int main()
{
	//new[]在给内置类型申请空间时,不会存储元素个数,因为自定义类型delete[]时没有析构函数可以调用,free不会出错
	//以内置类型int为例
	int* ptr = new int[10];
	delete ptr;
	return 0;
}

As in the above code, after executing delete ptr, you can see that this piece of continuous space is released.

2. Custom type

Such operations as structures, enumerations, unions in C language, and classes in C++ are problematic.

class A
{
public:
	int _sum;
	~A()
	{
		cout << "调用析构" << endl;
	}
};
int main()
{
	A* ptr = new A[10];
	delete ptr;
	return 0;
}

When executing delete ptr, the program only outputs a sentence of "calling destructor", and then terminates abnormally.

principle:

The principle of new/new[] and delete/delete[]: new and delete actually encapsulate two global functions operator new and operator delete at the bottom layer, while operator new and operator delete encapsulate malloc() and operator delete respectively at the bottom layer free().

Why built-in types don't fail:

Take the built-in type int in the above code as an example. When new int[10] is used, malloc actually applies for space for 10 int-type data at the bottom layer. When deleting, the bottom layer is directly released with free, which is obviously no problem.

Why does the custom type go wrong:

Take class A in the above code as an example. When new A[10] is handled differently from int, for a custom type, new will call the constructor of class A after applying for space with malloc at the bottom layer. For each Elements are initialized and so on.

It should be noted here that when the type allocated by new[] is a custom type, new[] will allow malloc to apply for 4 more bytes when allocating space, and new[] returns the return address of the underlying malloc backwards. Shift the address of 4 bytes, as shown in the figure below:

j

Specifically, when it comes to new int[10], malloc should have applied for 10 A-type spaces, that is, 40 bytes, but malloc applied for 44 bytes, and the pointer returned by new is the pointer returned by malloc. 4 bytes of address.

These 4 bytes store the number of applied custom elements, in order to determine the calling of the destructor.

Then it was said that free itself can know how much space to release by passing in a pointer, so is it unnecessary to add these 4 bytes to store the number of elements?

It is necessary to talk about the principle of delete[]: delete[] will return the address of the pointer returned by new[] offset by 4 bytes forward to free, so free can correctly release the entire space.

The correct match of A* ptr = newA[10] is delete[]ptr, then the pointer obtained by delete[] at this time is the return value of new[] (not the first address of the space requested by malloc), so the underlying free cannot release this space through this pointer, but delete[] also shifts the address of the pointer returned by new forward by 4 bytes to free.

The extra 4 bytes hold the function of the number of custom type elements:

An object needs to call a destructor to clean up some resources before releasing space, so the problem arises, delete[] does not pass in parameters like new A[10], how does delete[] know how many times to call the destructor. In fact, the number of elements stored in the extra 4 bytes is used to let delete[] know how many times to call the destructor.

2. Use delete[] to release the space requested by new

1. Built-in types

When the type is a built-in type, delete[] is the same as delete, so there is no error.

int main()
{
	int* ptr = new int();
	delete[] ptr;
	return 0;
}

2. Custom type

There will be an error, the reason is also mentioned above, when the type is a custom type, delete[], p will first determine the number of times to call the destructor according to the number of elements in the first 4 bytes of p, and call the destructor first function, the first 4 bytes of memory is illegal memory, and the value in it is random, so it will call the destructor many times, and then give the address of the first 4 bytes of the received pointer p to the underlying free, but pay attention to the The previous 4 bytes, these 4 bytes are not applied for space

Since free cannot release the space that has not been applied for, free will make an error, and free cannot know how much space to release through the address of the first 4 bytes of p, and it will also make an error (free can only know what to use through the address returned by malloc free up much space).

class A
{
public:
	int _sum;
	~A()
	{
		cout << "调用析构" << endl;
	}
};
int main()
{
	A* ptr = new A();
	delete[] ptr;
	return 0;
}

4. 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 .

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)
		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)

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 will return directly. Otherwise, it will execute the countermeasures provided by the user for insufficient space. If the user provides this measure, it will continue to apply, otherwise it will throw abnormal. operator delete finally releases space through free.

5. 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 it fails to apply for space, 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 space of the object

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

6. Locating the new expression

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

Use the format:

new(place_address) type或者new(place_address) type(initializer-list)

place_address must be a pointer, initializer-list is the initializer list of type

scenes to be used:

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;
};
// 定位new/replacement new
int main()
{
	// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
	A* p1 = (A*)malloc(sizeof(A));
	new(p1)A;  //注意:如果A类的构造函数有参数时,此处需要传参
	p1->~A();
	free(p1);
	A* p2 = (A*)operator new(sizeof(A));
	new(p2)A(10);
	p2->~A();
	operator delete(p2);
	return 0;
}

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. For custom types, 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 a space type

5. When malloc fails to apply for space, NULL is returned, because it must be judged as empty when used, new does not need it, but new needs to catch exceptions

6. When applying for a custom type object, malloc/free will only open up space to release the 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 constructor before releasing the space. Call the destructor to clean up the resources in the space

7. Memory leaks                  

What is a memory leak, and the harm of a memory leak?

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 errors. A memory leak does not refer to the physical loss of memory, but rather the loss of control over the memory segment due to a design error after the application allocates a certain segment of memory, resulting in a waste of memory.

Hazards of memory leaks: For short-term programs, as long as the program ends normally (execute return 0 statement), the operating system will reclaim memory, and there will be no memory leaks, but long-running programs have memory leaks, which have a great impact, such as the operating system , background services, etc., memory leaks will cause the response to become slower and slower, and eventually get stuck.

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 Leak:

Heap memory refers to a piece of memory allocated from the heap through malloc/calloc/realloc/new according to the needs of 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 leaks:

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 reduce system performance and cause 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.

Memory leak detection tool under Linux:

 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 has been applied for. But if you encounter an exception, even if you pay attention to the release, there may still be problems, and smart pointer management is required to ensure it.

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

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

4. If something goes wrong, use the memory leak tool to detect it.

Summarize:

Memory leaks are very common, and there are two solutions: 1. Pre-prevention type, such as smart pointers 2. Post-event error detection type, such as leak detection tools

Guess you like

Origin blog.csdn.net/qq_66767938/article/details/130311962