Why do memory leaks occur

Memory leak

Memory leak (English memory leak, occasionally too Memory hole or short memleak) are errors in a computer program that allow a running process to occupy a memory area, but can neither release nor use it in the course of execution.

Problem

Since unlimited growth of the exclusive use of a limited resource is impossible, a memory leak poses a problem in software development. In multitasking environments, this can also affect other processes that do not have a memory leak themselves. In any case, if the running time is long enough, the memory leak will lead to undesirable phenomena. At the end of the process, its memory requirements are also given up in modern operating systems.

The question of whether a program has a memory leak cannot be decided because of Rice's theorem.

possible solutions

The operating system is only able to provide a remedy to a very limited extent, since it usually cannot have a sufficiently deep insight into the internal data structures of the processes. It is easy to implement the limitation of the memory claim to a value that should meet the demands of the respective process, so that in the event of a memory leak at least no other processes are impaired. Other approaches are also conceivable in which heuristics are used to decide which of the processes is the one that could have a memory leak in order to then end it.

A basic distinction can be made between two alternatives for the possible approaches to memory management:

  1. Automatic garbage collection
  2. Explicit memory release

There are the following approaches to avoiding memory leaks:

  1. Analysis of the graph from memory areas (nodes) and references (edges)
  2. Analysis of the source code similar to the formal verification
  3. Analysis of specific runtime situations as part of a software test

These alternatives are briefly considered below.

Automatic garbage collection

If the runtime environment used provides automatic garbage collection, memory can be allocated and used by a process. As soon as the runtime environment determines that an occupied memory area is no longer reachable is released again. In this context, no longer accessible means that there is no longer a valid variable reference for the occupied memory area. In the general case, however, this approach is insufficient, since incorrectly stored references cannot be recognized.

Explicit memory release

In contrast to automatic garbage collection, the application developer can implement the memory management himself in other programming languages ​​(C, C ++ or Pascal). This means that memory areas can be requested, used and then released again dynamically. The memory allocation takes place explicitly at places provided by the programmer and is not linked to the existence of a variable reference, which creates sources of error in addition to the difficulties of automatic memory cleanup.

Systematic tests

Systematic testing with the help of appropriate tools that can determine with a certain degree of certainty which memory areas are to be assigned to a memory leak can avoid the problems of formal verification.

A well-known such tool is Valgrind. It examines (in Linux systems) the memory usage of a process at runtime. In a few obvious cases, it may be sufficient to just observe the memory consumption of a process over time.

Formal verification

In particular, memory leaks can also be discovered through a correctness proof. However, this procedure is very time-consuming and requires highly qualified personnel. The question, which specializes in memory leaks, can also be examined with the aid of a computer.

Examples

The best-known example of a lack of automatic memory management is the C language. New memory is requested in C programs by the function. It supplies a pointer to the beginning of the corresponding memory area. This reference is necessary in order to release the memory again using a suitable code (using the function). If the pointer is lost or changed, the process can no longer access this memory and thus cannot release it.

Example for people without programming knowledge

This example is intended to show people without programming knowledge how a memory leak can occur. It's a freely thought-out example.

The example shows an excerpt from a program in pseudocode for controlling an elevator. This part of the elevator program is always executed when someone in the elevator presses a button to select a floor.

When a button is pressed: Reserve memory to store the desired floor number. Write the floor number in the reserved memory. Is the car already on the desired floor? If so, there is nothing to be done: End subroutine Otherwise: Wait until the elevator has no further tasks. Drive to the desired floor. Release the memory in which the desired floor number was stored

This program has a memory leak. If a floor has been selected and the elevator is already on this floor, the memory reserved in the second program line will never be released again.

This has no immediate effect. Elevator users seldom press the button for the floor they are on, and the elevator may have so much free memory that it would work hundreds or thousands of times without a problem. However, at some point the elevator could have used up all of its memory. This could take months or years and is therefore very difficult to find out by testing.

The consequences in this case would be uncomfortable. The lift could stop responding to user input and stop moving. If the program also needs memory to open the doors, the fault could cause someone to be locked in the elevator, as there would be no more memory available to open the doors.

The memory leak would only exist as long as the program is running. If the lift were to be switched off, the program would stop. After reactivation, the program would restart, the memory would be released again and the slow process of memory leakage would begin again.

C.

The following example shows the development of a memory leak using a program implemented in C:

int main (void) {int * a, * b; / * pointer to a memory area interpreted as an integer * // * reserve memory for pointers. * / a = malloc (sizeof (int)); b = malloc (sizeof (int)); * a = 5; / * Writes the value "5" into the memory area referenced by a * / * b = 3; / * Writes the value "3" into the memory area referenced by b * / a = b; / * Assigns pointer a to the destination of pointer b. * // * a and b now refer to the same memory area. The * memory previously referenced by a is thus orphaned and can no longer be released. At this point * there is a memory leak. * / free (b); / * Releases the memory referenced by b * / free (a); / * Leads to an error because the area referred to by a * has already been released (in the previous line) * / return EXIT_SUCCESS;}

From the step onwards, it is nearly impossible to access the storage area previously referenced by a. The area can usually no longer be released by the program.

C ++

Memory leaks under C ++ occur in the same way as under C. Another source of errors that does not occur under C is the use of the wrong one deleteOperator. If an array of objects is requested, this array must be combined with the delete []Operator can be released. The normal one deleteOperator has the effect that only the destructor of the first object is called, but the memory of the entire array is released. The destructor is not called for all other objects. If dynamically allocated memory is released in the destructor (as in the example), memory leaks occur. Only the delete []-Operator calls the destructor for all objects of the array and then releases the memory of the array.

class Object {int * m_value; public: Object () {m_value = newint; * m_value = 42;} virtual ~ Object () {* m_value = 0; delete m_value; m_value = NULL;}};

Correct: Memory for 5 Objects requested of type int. The memory is completely released

int * pi = newint [5]; delete [] pi;

Not correct: Only the destructor of the object po [0] is called. Memory leak because destructor is not called for po [1] to po [4].

Object * po = new Object [5]; delete po;

Correct: The destructors of all five objects are called. All allocated memory is released.

Object * po = new Object [5]; delete [] po;

Automatic garbage collection

The following example shows the failure of the automatic garbage collection approach in pseudocode:

PROCEDURE main LIST a LOOP a.INSERT (1) IF DATE () == START_TIME () + 10000 THEN BREAK END END

You can clearly see here that the storage requirement is constantly growing. Assuming that the increasing memory requirement itself is not the purpose of the program, it is a memory leak because there is no read access to the list entries. This error would be quite easy to detect by static analysis, while the graph of references and memory areas does not reveal the error.

Web links