An overview,

In computer science, a Memory leak is the failure of a program to release Memory that is no longer in use because of an oversight or error

This does not mean that memory is physically gone, but rather that an application loses control of memory before releasing it due to a design error

The program needs memory to run. The operating system or runtime must supply memory whenever the program requests it

For continuously running server processes, memory that is no longer needed must be released in a timely manner. Otherwise, the memory footprint increases, which can affect system performance at best or cause process crashes at worst

In C, memory leaks are a common occurrence because memory is managed manually.

char * buffer;
buffer = (char*) malloc(42);

// Do something with buffer

free(buffer);
Copy the code

The malloc method is used to allocate memory. After using the malloc method, you must free the memory.

This is cumbersome, so most languages provide automatic memory management to ease the programmer’s burden, which is called “garbage collection.”

Second, garbage recycling mechanism

Javascript has an automatic GC: Garbage Collecation, which means that the execution environment is responsible for managing the memory used during code execution

How it works: The garbage collector periodically (periodically) finds variables that are no longer in use and frees their memory

There are usually two ways to do this:

  • Mark clear
  • Reference counting

Mark clear

JavaScript’s most commonly used garbage collection mechanism

When a variable enters the execution environment, mark it as “enter environment”. The memory occupied by variables entering the environment cannot be released. When a variable leaves the environment, it is marked as “out of the environment”.

When the garbage collector runs, it marks all variables stored in memory. It then strips out all variables in the context, and all variables referenced by variables in the context

Variables tagged after this point are deleted because they are not accessible to any variables in context

The garbage collector then does a memory cleanup, destroying all tagged values and reclaiming their memory

Here’s an example:

var m = 0,n = 19 // mark m,n,add() to enter the environment.
add(m, n) // mark a, B, and c as entering the environment.
console.log(n) // A, B, and C are marked as leaving the environment and waiting for garbage collection.
function add(a, b) {
  a++
  var c = a + b
  return c
}
Copy the code

Reference counting

The language engine has a “reference table” that holds the number of references to all resources (usually values) in memory. If the number of references to a value is zero, the value is no longer needed, so it can be freed

If a value is no longer needed but the number of references is not zero, the garbage collection mechanism cannot free the memory, resulting in a memory leak

const arr = [1.2.3.4];
console.log('hello world');
Copy the code

In the above code, the array [1, 2, 3, 4] is a value that consumes memory. The variable arr is the only reference to this value, so the number of references is 1. Although the following code does not use ARR, it continues to consume memory

If you want this block of memory to be freed by garbage collection, just set it as follows:

arr = null
Copy the code

By setting arr to null, the array [1,2,3,4] is dereferenced, the number of references becomes zero, and is garbage collected

summary

Having a garbage collection mechanism doesn’t mean you don’t have to worry about memory leaks. Values that take up a lot of space need to be checked for references once they are no longer needed. If so, you must manually dereference it

Common memory leaks

Unexpected global variables

function foo(arg) {
    bar = "this is a hidden global variable";
}
Copy the code

Another unexpected global variable might be created by this:

function foo() {
    this.variable = "potential accidental global";
}
// foo calls itself, this refers to the global object (window)
foo();
Copy the code

Using strict patterns above, you can avoid unexpected global variables

Timers also often cause memory leaks

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        // Process node and someResource
        node.innerHTML = JSON.stringify(someResource)); }},1000);
Copy the code

If the Node element is removed from the DOM, the timer will still exist, and since the callback contains a reference to someResource, the someResource outside the timer will not be released

Including closures, which we talked about earlier, that hold local variables in a function and keep them from being released

function bindEvent() {
  var obj = document.createElement('XXX');
  var unused = function () {
    console.log(obj, 'Obj inside the closure will not be freed');
  };
  obj = null; // The solution
}
Copy the code

Not cleaning up references to DOM elements also causes memory leaks

const refA = document.getElementById('refA');
document.body.removeChild(refA); // dom is deleted
console.log(refA, 'refA'); // But there are references that can console out the entire div that are not reclaimed
refA = null;
console.log(refA, 'refA'); // Dereference
Copy the code

This includes using event listener addEventListener and removeEventListener to cancel event listener when not listening