Memory leaks ... Clarification (hopefully)

Can someone please help me understand the concept of memory leak and how to promote / prevent its specific data structures (e.g. linked lists, arrays, etc.). Some time ago, I was taught twice by different people, which confused me a bit due to differences in teaching methods.

+4
source share
5 answers

Wikipedia has a good description of memory leaks . The definition given here is:

A memory leak, in computer science (or leakage, in this context), occurs when a computer program consumes memory but is unable to release it back to the operating system. 

For example, the following C function emits memory:

 void leaky(int n) { char* a = malloc(n); char* b = malloc(n); // Do something with a // Do something with b free(a); } 

The above function leaks n bytes of memory because the programmer forgot to call free(b) . This means that the operating system has n bytes less memory to satisfy further malloc calls. If a program causes leaky many times, the OS may eventually run out of memory that it could allocate for other tasks.

As for the second part of your question, there is nothing internal for data structures that makes them a memory leak, but careless implementation of the data structure can lead to a memory leak. As an example, consider the following function that removes an item from a linked list:

 // I guess you could figure out where memory is leaking and fix it. void delete_element(ListNode* node, int key) { if (node != NULL) { if (node->key == key) { if (node->prev != NULL) { // Unlink the node from the list. node->prev->next = node->next; } } else { delete_element(node->next, key); } } } 
+7
source

I agree with Vijay's answer for the most part, but it's important to note that leaks occur when references to heap blocks (pointers) are lost. Two common reasons:

1 - Loss of pointer area

 void foo(void) { char *s; s = strdup("UNICORNS ! ! !"); return; } 

In the above, we have lost the region of the pointer s , so we have absolutely no way to free it. This memory is now lost in the (address) space until the program exits and the virtual memory subsystem recovers everything that the process had.

However, if we just changed the function to return strdup("UNICORNS ! ! !"); , we will still refer to the block allocated by strdup ().

2 - Reinstalling pointers without saving the original

  void foo(void) { unsigned int i; char *s; for (i=0; i<100; i++) s = strdup("UNICORNS ! ! !"); free(s); } 

In this example, we lost 99 references to blocks that s pointed once, so we actually only release one block at the end. Again, this memory is now lost until the OS restores it after the program exits.

Another common misconception is that the memory available for program exit is leaked if the program does not release it before exiting. This did not happen long. A leak occurs only when it is not possible to dereference a previously allocated block to free it.

It should also be noted that working with the static storage type is slightly different, as discussed in this answer .

+2
source

In principle, a memory leak occurs when a program allocates memory and does not free it, even if it is no longer needed.

  • In languages ​​with manual memory management, for example, C, this happens when a program cannot explicitly free heap memory, i.e. a programmer must always do something to avoid memory leaks.
  • In basic garbage collection languages, such as Java, this happens when a program inadvertently stores references to objects that are no longer needed. Very often this happens when such objects are added to the "global" collection and then "forgotten" (especially when the addition occurs implicitly).

As you can see from the second point, collections as a whole tend to focus on a memory leak, because they are not obvious what they contain, doubly when they are supported internally by a long-lived object.

A prototype memory leak is a cache (i.e., a collection that is supported implicitly) stored in a static variable (i.e., as long as possible).

+1
source

Vijay's answer shows how to produce a memory leak. But finding a leak can be quite a challenge as soon as your program grows in a few lines of code.

If you are on Linux, valgrind can help you find leaks.

On Windows, you can use CRT Debug Heap , which displays what leaked, but not where it was allocated. To display where a memory leak was allocated, you can use the Memory Validator , which is pretty painless to use: either run your program under the hood of the Memory Validator or connect to a running process. No changes to the sources are required. They provide a 30-day trial version that is fully functional.

0
source

I cannot add to what others have said in terms of defining memory leaks, but I can give you some notes on when a memory leak can occur.

The first case that comes to mind is a function that performs the selection:

 int* somefunction(size_t sz) { int* mem; mem = malloc(sz*sizeof(int)); return mem; } 

There is nothing inherited in writing a function in this way. This is very similar to malloc. The problem is that you are starting now:

 int* x = somefunction(5); 

And it’s easy to forget, now it’s not malloc to free x. Again, there is nothing in it that means that you will forget, but my experience tells me that this is something that I and others do not notice.

A good strategy to get around this is to specify in a function calling this distribution. So, call somefunction_alloc function.

The second case that comes to mind is topics, especially fork() , because the code is all in one place. If you carefully code functions, several files, etc., you almost always avoid mistakes, but remember that everything should be free in some area, including those that were allocated both fork () and pre fork. Consider this:

 int main() { char* buffer = malloc(100*sizeof(char)); int fork_result = fork(); if ( fork_result < 0 ) { printf("Error\n"); return 1; } elseif ( fork_result == 0 ) { /* do child stuff */ return 0; } else { /* do parent stuff */ } free(buffer); return 0; } 

There is a small mistake here. The parent will not skip any memory, but the child does this because it is an exact copy of the parent, including the heap, but it exits before freeing anything. Free should occur on both codes. Similarly, if the plug fails, you still will not be released. It's easy to miss something when you write such code. The best way is to create an exit code variable, for example int status = 0; , and change it where errors occurred, and not use the return in any structure, but allow the paths of the child and parent codes to continue until the end of the program, as they should.

However, threads and markup always make debugging more difficult due to their nature.

0
source

Source: https://habr.com/ru/post/1338099/


All Articles