This is a simplified recreation of a problem I have been encountering with a personal project I have been working on.
I am trying to create a heap array of pointers which are pointing to heap allocated structs. The issues there seems to be a memory leak somewhere and I am not exactly sure why that might be.
I using Task manager to track the amount of ram my program is using.
Here is the code written in C for my Struct allocation, I am using codeblocks as my IDE which is using the GNU GCC Compiler if that impacts anything.
#include <stdio.h>
#include <stdlib.h>
//My Struct as an example
typedef struct dog {
int Age;
int Weight;
} Dog;
typedef Dog* DGPtr;
// A function to Create a heap allocated instance of this struct
DGPtr CreateDog(int age, int weight){
DGPtr Ptr = (DGPtr) malloc(sizeof(Dog));
Ptr->Age = age;
Ptr->Weight = weight;
return Ptr;
}
int main(){
// I take inputs every now and then to be able to check in task manager where my ram is at during the process
char inp[10];
int size = 100000000;
DGPtr myPoint = CreateDog(5,5);
//Step 1 Malloc but no write
scanf("%s", inp);
DGPtr* ptrArr = malloc(sizeof(DGPtr) * size);
//Step 2 Write to malloc
scanf("%s", inp);
for (int i = 0; i < size; i++){
ptrArr[i] = (DGPtr) malloc(sizeof(Dog) * 1);
ptrArr[i]->Age = 7;
ptrArr[i]->Weight = 7;
}
//Step 3 Free All pointer in array
scanf("%s", inp);
for (int i = 0; i < size; i++){
free(ptrArr[i]);
}
//Step 4 Free Array
scanf("%s", inp);
free(ptrArr);
scanf("%s", inp);
return 0;
}
Here is the code for the primitive allocations:
int main(){
char inp[10];
int size = 100000000;
//Step 1 Malloc but no write
scanf("%s", inp);
int** ptrArr = malloc(sizeof(int *) * size);
//Step 2 Write to malloc
scanf("%s", inp);
for (int i = 0; i < size; i++){
ptrArr[i] = malloc(sizeof(int));
}
//Step 3 Free All pointer in array
scanf("%s", inp);
for (int i = 0; i < size; i++){
free(ptrArr[i]);
}
//Step 4 Free Array
scanf("%s", inp);
free(ptrArr);
scanf("%s", inp);
return 0;
}
I have tried a the same experiment with primitive datatypes, in other words I tried it with Integers. Recreating an int pointer array and then allocating integer arrays to those pointers. I do also get a issue where the memory is being leaked just a bit less than when I do it with a basic struct.
And Yes the memory leak scales with the size of the array I am allocating. It does appear to scale more or less linearly, as when I ran the test and then scaled the array size up by a factor of 10 the memory leak also scaled up by a factor of 10 well not exactly but very close like 9.something.
Here are some images of my task manager as I run the experiments:
Before the allocations has started for struct pointer array: enter image description here The memory allocation still looks the same after mallocing but not writing to the mem block At step 3 where we have just written to it we have this: enter image description here As you can se we are using a bit of memory At step 4 where we have gone through the pointer array freeing all the pointers in that array we are left with this: enter image description here At the end after freeing the array of pointers we are left with this enter image description here
The issue is as you can see we are still using 11.2 MB of RAM where before this entire operation was carried out we only used 0.3 MB of RAM.
Can someone please explain why at the end of the process we still have a lot more ram in use then before? And where that ram might be and how to get back to the previous ram usage level.
The only leak in the first program is DGPtr myPoint = CreateDog(5,5);
. Either free()
it or leave it out as you don't use it.
The effect that you observe is probably that free()
doesn't return (all) the memory to the OS. This is an optimization to make subsequently allocations faster.
Here's the minimal working program without leaks and minimal error handling:
#include <stdio.h>
#include <stdlib.h>
struct dog {
int Age;
int Weight;
};
struct dog *CreateDog(int age, int weight) {
struct dog *dog = malloc(sizeof *dog);
if(!dog) return NULL;
dog->Age = age;
dog->Weight = weight;
return dog;
}
int main(void) {
int size = 10;
struct dog **dogs = malloc(sizeof *dogs * size);
if(!dogs) {
fprintf(stderr, "malloc failed\n");
return 1;
}
for (int i = 0; i < size; i++) {
dogs[i] = CreateDog(7, 7);
if(!dogs[i]) {
fprintf(stderr, "malloc failed\n");
return 1; // note: dogs[j] for j < i and dogs leak for OS to cleanup.
}
free(dogs[i]);
}
free(dogs);
}
and example run from valgrind:
==431867== HEAP SUMMARY:
==431867== in use at exit: 0 bytes in 0 blocks
==431867== total heap usage: 11 allocs, 11 frees, 160 bytes allocated
==431867==
==431867== All heap blocks were freed -- no leaks are possible