Summary of memory management in C / C + +

Time:2020-11-21

preface

The memory development method we were familiar with at first:

  • Int Val = 20: 4 bytes in stack space
  • Char array [10]: create 10 bytes of continuous space on the stack space

There are two characteristics in the way of opening up space

  • The size of space development is fixed.
  • When an array is declared, the length of the array must be specified, and the required memory is allocated at compile time.

But for space requirements, not only the above situation, sometimes we need to know the empty size when the program is running, then the static way to open up space can not meet, we can only try dynamic memory development at this time.

This blog will take you to sort out the memory management in C / C + +.

1: C / C + + memory distribution

Memory segmentation is a computer management mechanism

1. Stack, also known as stack, stores non static local variables, function parameters, return values, etc., and the stack grows downward. The processor has concentrated instructions and high efficiency, but the capacity of memory allocation is limited. (these storage units are released automatically after the function is executed)
2. Memory mapping segment is an efficient IO mapping method, which is used to load a shared dynamic memory library. Users can use the system interface to create shared memory for inter process communication.
3. The heap is used for dynamic memory allocation while the program is running, and the heap grows upward. (it is generally distributed and released by human. If there is no artificial release, it may be recovered by OS at the end of the program.)
4. Data segment stores global data and static data. (automatically released by the system after the end of the program)
5. Code segment stores executable code and read-only constant.

be careful:
The stack area grows downward, and the first space address is larger than the later one. (int a = 10,int b = 20,&a>&b)
The heap area grows upward, but it is not guaranteed that the space address opened later is larger than the space address opened first, because there is artificial space release in the heap area.

2: Memory management in C language

C language provides dynamic memory function for dynamic development of memory: malloc, calloc, realloc, free

2.1 malloc

Function function

void ✳ malloc(size_ T size the size of space in bytes)

Take a chestnutint* ptr = (int*) malloc(sizeof(int)*10);

Malloc requests a continuous free space of size from memory and returns a pointer to this space.

Functional properties
1. Successful development, return a pointer to the space.
2. If the development fails, a null pointer is returned. Therefore, the return value of malloc must be checked.
3. The type of return value is void A kind of The malloc function does not know the data type of the space to be opened, and it is up to the user to decide when using it.
4. If the parameter size is 0, the behavior of malloc is not defined in the standard and depends on the compiler.

2.2 calloc

Function function

void ✳ calloc(size_ T num number of elements, size_ T size the size of space in bytes)

Take a chestnutint* ptr = calloc(10,sizeof(int));

Calloc creates a contiguous space for num size elements in memory and initializes each byte of the space to 0.

Functional properties
1. Successful development, return a pointer to the space.
2. If the development fails, a null pointer is returned. Therefore, the return value of calloc must be checked.
3. The type of return value is void A kind of The calloc function does not know the data type of the space to be opened, and it is up to the user to decide when using it.
4. Calloc will initialize each byte of the requested space to 0 before returning the address (calloc is applicable to the initialization of the requested space content)

Note: it is not a good thing to initialize the applied space. When we want to apply for a very large space, initialization will waste a lot of time.

2.3 realloc

Function function

void ✳ realloc(void ✳ Memory address to be adjusted by PTR, size_ T size after adjustment)

Take a chestnutint* p = NULL; p = realloc(ptr,1000); if(p!=NULL)-> ptr = p;

Realloc can flexibly adjust the size of dynamically opened memory space.

Functional properties

1. The return value is the starting position of the memory space after adjustment.
2. Realloc will move the data in the original memory space to a new space on the basis of adjusting the size of the original memory space.

There are two situations when realloc adjusts memory space
Case 1: there is enough space after the original space

Add space directly after the original memory space, the data of the original space does not change

Case 2: there is not enough space after the original space

In the heap space to find a suitable size of continuous space to use, so that the function returns a new memory address.

Common dynamic memory errors

1. Dereference to null pointer.

2. Cross border access to dynamic open space.

3. Free is used for non dynamic memory.

4. Release a piece of dynamic open memory.

5. Free the same block of memory multiple times.

6. Dynamic open memory forget to release.

The above errors are very common, so we must be very careful when operating memory.

Examples of typical memory leaks


int main(){
 int* p = (int*)malloc(sizeof(int));
 p = (int*)malloc(sizeof(int));
 free(p);  
 p = NULL;
}

In this example, we explicitly released the memory, but it also caused a memory leak. This is because we applied for the memory space twice, but only released it once by using the same pointer to receive it. Therefore, the memory leak was caused.

After dynamic memory allocation, we must not forget to release the memory space after using it, and assign the pointer to null. This is very important, otherwise it will cause memory leakage and wild pointer, which will have a great impact on the program.

3: Memory management in C + +

C language memory management mode can continue to be used in C + +, but there are some places that can’t be done and it is troublesome to use it. Therefore, C + + puts forward its own memory management method: dynamic memory management through new and delete operators

In C + +, we use new to request memory and delete to release memory.

3.1 memory allocation and release of built-in types

New, like malloc, will open up space on the heap. At the same time, we need to release the memory manually. However, the writing method of new is simpler and easier to understand. At the same time, we can also initialize the variables of a single application.

Give me a chestnut to help you understand

#include <iostream>
using namespace std;
int main(){
 Int * a = New Int; // equivalent to int * a = (int *) malloc (sizeof (int));
 Int * b = New Int [10]; // equivalent to int * b = (int *) malloc (sizeof (int) * 10);
 Int * C = new int (10); // new can also initialize built-in types
 cout << *c << endl;
  
 Delete a; // is equivalent to free (a);
 Delete [] B; // is equivalent to free (b); (for space release of multiple variables, delete [] is used.)
 Delete C; // is equivalent to free (c);
 
 return 0;              
}

3.2 memory allocation and release of custom types

For the memory allocation and release of user-defined types, new can not only call the specified constructor manually when allocating memory, but also call the default constructor automatically when allocating the space of multiple objects. Delete will also call the destructor automatically, but malloc and free can’t do this. Therefore, it can be understood that the allocation of malloc and free is only a space of the same size as the class, and can not be called an object, while the allocation of new and delete can be regarded as an object.

#include <iostream>                 
#include <stdlib.h> 
using namespace std;              
class Stu{                          
public:        
 Stu(){         
   cout << "default building" << endl;
  }
        
  Stu(int num, string name):_num(num), _name(name){        
   cout << "custom building" << endl;
  }
         
  ~Stu(){                
   cout << "destroying" << endl;
  }
private:
 int _num;
 string _name;    
};
       
int main(){     
 cout << "malloc:" << endl;
 Stu* a = (Stu*)malloc(sizeof(Stu));
 cout << "new:" << endl;
 Stu * b = new stu (1, "Zhang San");
 cout << "malloc:" << endl;
 Stu* c = (Stu*)malloc(sizeof(Stu) * 5);
 cout << "new:" << endl;
 Stu* d = new Stu[5];
 cout << "free:" << endl;
 free(a);
 cout << "delete:" << endl;
 delete b;
 cout << "free:" << endl;
 free(c);
 cout << "delete:" << endl;
 delete[] d;
}

Operation results:

 malloc:
 new:
 custom building
 malloc:
 new:
 default building
 default building
 default building
 default building
 default building
 free:
 delete:
 destroying
 free:
 delete:
 destroying
 destroying
 destroying
 destroying
 destroying

3.3 implementation principle of new and delete

New and delete are actually defined as two operators in C + +. When we use these two operators, they will call the global functions operator new and operator delete at the bottom.

operator new

The source code of operator new in the underlying implementation

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
 //If the memory request fails, bad will be thrown here_ Alloc type exception
 static const std::bad_alloc nomem;
 _RAISE(nomem);
 }
 return (p);
}

operator delete

The source code of operator delete in the underlying implementation


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

From the source code, we can see that operator new and operator delete use malloc and free to allocate memory at the bottom, so we can say that new and delete are just a layer of encapsulation of malloc and free.

For built-in types

If you are applying for a built-in type of space, new and malloc, delete and free are basically similar. The difference is that new / delete requests and releases space for a single element, new [] and delete [] apply for continuous space. Moreover, new will throw an exception when space application fails, and malloc will return null.

For custom types

1. Principle of new: call operator new to apply for space, and call constructor to complete initialization.
2. The principle of delete: call the destructor to complete the cleaning, and call operator delete to release space.

4: Classic interview questions

Similarities and differences between new | delete and malloc | free

Similarities:
New, delete, malloc, and free all open up space from the heap and need to be released manually by the user.

difference:
1. New and delete are operators, malloc and free are functions.

New malloc can not apply for space initialization.

3. Null is returned if malloc fails to apply for space, and an exception will be thrown if new space application fails.

4. For custom type, new and delete will call constructor and destructor automatically.

5: Memory leak

Concept: memory leak refers to the situation that memory that is no longer used by the program is not released due to negligence or error.

Harm: memory leak of long-term running program will waste space, such as operating system, background service, etc
As a result, the response becomes slower and slower, and finally gets stuck.

Take a chestnut to help understand:

void MemoryLeaks(){
 //1. The memory has been requested and forgotten to be released
 int* p1 = (int*)malloc(sizeof(int));
 int* p2 = new int;
 
 //2. Abnormal safety
 int* p3 = new int[10];
 Func(); // in this case, the func function throws an exception, resulting in delete [] P3 not being executed and P3 not being released
 delete[] p3;
}

5.1 classification of memory leaks

Heap memory leak

A block of memory allocated from the heap by malloc / calloc / realloc / new according to the need of program execution,
After use, it must be deleted by calling the corresponding free or delete. If the program design error causes this part of memory not to be released, then this part of space will not be able to be used in the future, which will lead to heap memory leak.

System resource leakage

The program uses the resources allocated by the system, such as socket, file descriptor, pipeline and so on, but does not use the corresponding function to release, resulting in the system
The waste of resources can lead to the decrease of system efficiency, the instability of system execution and the leakage of system resources.

5.2 how to detect memory leaks

Memory leak detection in Linux
valgrind、mtrace、dmalloc、memwatch、mpatrol、dbgmem、Electric Fence

Memory leak detection in Windows
VLD

5.3 how to avoid memory leak

1. Good design specifications in the early stage of the project, develop good coding standards, and remember to release the memory space applied for matching.

2. Using raii idea or intelligent pointer to manage resources.

5.4 how to apply 4G space on heap

Reason: application failure is generally due to insufficient process address space.

Solution: switch to 64 bit process address space.

This article on C / C + + memory management summary of the article introduced here, more related C + + memory management content, please search the previous articles of developeppaer or continue to browse the related articles below, I hope you can support developeppaer more in the future!