The allocation of dynamic memory is very simple, we need to call a library function (malloc, calloc, etc.) to allocate the memory at runtime (from the heap), after using the allocated memory it again called a different function (free, realloc) to release the allocated memory.
The allocation of memory at the runtime is great features because it resolves a lot of problems that are faced by the developer at the time of application running. We know that in the real world everything has a positive and negative effect, in this article we will see the problem which generally arises if we do not use the dynamic memory properly.
The problem with dynamic memory allocation is that it is not deallocated itself, developer responsibility to deallocate the allocated memory explicitly. If we cannot release the allocated memory, it can because of memory leak and make your machine slow. It is not only causes of memory leak but if you do not use the memory allocation properly, it can be the cause of the of memory fragmentation that is also serious issues.
There is one major problem with dynamic allocation, if you freed the memory before completed its task, then it can create hidden bug which is difficult to identify and can be a cause of the system crash or unpredictable value.
In below section, I am describing some problem of dynamic memory allocation with their solution.
Memory Leak
As we have described in the beginning of the article, a memory leak is a common and dangerous problem. It is a type of resource leak. In C language, a memory leak occurs when you allocate a block of memory using the memory management function and forget to release it.
Note: once you allocate a memory than allocated memory does not allocate to another program or process until it gets free.
Let’s take an example for the better understanding.
Suppose a device is receiving a response packet from the server and the length of the response packet is dynamic. In that situation, you need to create a dynamic buffer using the memory management function to store the response packets. The device is allocating the memory every time when it gets the signal of the response packet, but the problem is created when the developer forgets to free the allocated memory.
It might be the effect of this bug reflect after the receiving of 500 or more response packet (run out of memory) it depends on the storage of the device. It is very hard to predict this type of bug because if you reset the device (power off), it works fine like previously.
Let’s see a program,
In below program programmer forget to free the allocated memory, it can cause a memory leak.
int ReceivePacket(void) { char * pBuffer = malloc(sizeof(char) * iLenBuffer); /* Do some work */ return 0; /*Not freeing the allocated memory*/ }
Note: In C language, only programmer responsibility to deallocate the allocating memory.
For learning more, you can signup for the free trial of this popular c video course by Kenny Kerr.
Some important rules to avoid the memory leaks in c
-
Every malloc or calloc should have a free function
It is a golden rule to write the free function after each malloc (calloc). Suppose in an application you have required to create an array of character to store some dynamic data. You have to use memory management function (malloc or calloc) to allocate the memory. After writing the expression of malloc its good habits to write the free function corresponding to the allocating memory.
char *pInfoData = malloc(n *sizeof(char));
free(pInfoData);
Now start to write the code between malloc and free function.
char *pInfoData = malloc (n *sizeof(char));
//Do some work
free(pInfoData);
Sometimes we have required allocated memory throughout the application, in that situation we have to write the free function after just writing the malloc in the handler which is invoked at the ending of the application.
For example,
Suppose there is a callback function DeactivateHandler() that is invoked at the end of the application, so we have to write the free function in DeactivateHandler() just after writing the malloc.These techniques reduce the probability to forget to free the memory.
-
Create a counter to monitor allocated memory
It is a good technique to prevent the memory leaks. In this technique, we will create two global counters and initialize them with 0. In every successful allocation, we will increment the value of the counter1 (Allocate_Counter ) and after the deallocating the memory we will increment the counter2 (Deallocate_Counter). In the end of the application, the value of both counters should be equal.
This method helps you to track the status of allocated memory. To implement this technique we need to create three customize function, one for memory allocation and second for memory deallocation and last one to check the memory leak.
static unsigned int Allocate_Counter = 0; static unsigned int Deallocate_Counter = 0; void *Memory_Allocate (size_t size) { void *pvHandle = NULL; pvHandle = malloc(size); if (NULL != pvHandle) { ++Allocate_Counter; } else { //Log error } return (pvHandle); } void Memory_Deallocate (void *pvHandle) { if(pvHandle != NULL) { free(pvHandle); ++Deallocate_Counter; } } int Check_Memory_Leak(void) { int iRet = 0; if (Allocate_Counter != Deallocate_Counter) { //Log error iRet = Memory_Leak_Exception; } else { iRet = OK; } return iRet; }
-
Do not work on the original pointer
It is a good habit to work on a copy of the pointer, it preserves the address of allocating memory. If there is any accidental change occurred on the pointer, this technique helps you to get the actual address of allocating memory that is needed at the time of memory deallocation.
int *pBuffer = malloc ( sizeof(char) * n ); //Create copy of the pointer int *pTmpBuffer = pBuffer; // Do some work free (pBuffer);
-
Write the proper comments
I think it is good habits to write the comment at every section of the code. It always reminds you that what you did. It helps you if you read your code after some months or years.
-
Avoid the orphaning memory location
At the time of memory deallocation, we need to free the memory from child to parent that means a child will be free first. If we free the parent first, it can be a cause of memory leak.
For example,
In below code, the pointer to context structure is freeing first. So the pointer that is pointing to space for the information data become orphan and it can be a cause of memory leak.
typedef struct { void *pvDataInfo; }sContext; //Allocate the memory to pointer to context structure sContext *pvHandle = malloc(sizeof(sContext)); //Allocate the memory for Information data pvHandle-> pvDataInfo = malloc(SIZE_INFO_DATA); free(pvHandle); // pvDataInfo orphan
Carry the length of dynamically allocated memory
In C language, we can calculate a size of the static array using the sizeof operator but cannot calculate the size of the dynamic array. So It is also a great idea to carry the length of the dynamic array. This idea prevents you from many unwanted issues, to carry the length we need to allocate some extra space.It is my advice, whenever you use the technique then check that length of the array should not exceed the type of the array.
For example,
Suppose you need to create an integer array whose size is n. So to carry the array length of the array, you need to allocate the memory for n+1.
int *piArray = malloc ( sizeof(int) * (n+1) );
If memory is allocated successfully, assign n (size of the array) its 0 places.
piArray[0] = n;
or
* piArray = n;
Now it’s time to create a copy of original pointer but to left one location from the beginning.
int * pTmpArray = piArray +1;
Note: if you are new, see this article arithmetic operation on the pointer.
Now, whenever in a program you ever required the size of the array then you can get from copy pointer.
ArraySize = pTmpArray[-1];
After using the allocated memory don’t forget to deallocate the allocated memory.
free (piArray);
Memory fragmentation
The memory management function is guaranteed that if memory is allocated, then it would be suitably aligned to any object which has the fundamental alignment. The fundamental alignment is less than or equal to the largest alignment that’s supported by the implementation without an alignment specification.
One of the major problems with dynamic memory allocation is fragmentation, basically, fragmentation occurred when the user does not use the memory efficiently. There are two types of fragmentation, external fragmentation, and internal fragmentation.
The external fragmentation is due to the small free blocks of memory (small memory hole) that is available on the free list but program not able to use it. There are different types of free list allocation algorithms that used the free memory block efficiently.
Consider a scenario where a program has 3 contiguous blocks of memory and the user frees the middle block of memory. In that scenario, you will not get a memory, if the required block of memory is larger than a single block of memory (but smaller or equal to the aggregate of the block of memory).
The internal fragmentation is the wasted of memory that is allocated for rounding up the allocated memory and in bookkeeping (infrastructure), the bookkeeping is used to keep the information of the allocated memory.
Whenever we called the malloc function then it reserves some extra byte (depend on implementation and system) for bookkeeping. This extra byte is reserved for each call of malloc and become a cause of the internal fragmentation.
For example, See the below code, the programmer may think that system will be allocated 8 *100 (800) bytes of memory but due to bookkeeping (if 8 bytes) system will be allocated 8*100 extra bytes. This is an internal fragmentation, where 50% of the heap waste.
char *acBuffer[100]; int main() { int iLoop = 0; while(iLoop < 100) { acBuffer[iLoop ] = malloc(8); ++iLoop; } }