A Memory Leak is a condition in which a program fails to release Memory that is no longer in use because of an oversight or error. If the location of the memory leak is critical, more and more unused memory may be held as the processing progresses, causing server response times to slow down and, in severe cases, memory to reach a certain limit (it could be the upper limit of the process, such as V8’s limit; It can also be the memory limit that the system can provide) that causes the application to crash. In traditional C/C++, there are memory leaks caused by Pointers, objects that are not released after they are used. However, languages that use virtual machines, such as Java and Javascript, use the Garbage Collection (GC) mechanism to automatically release memory, which greatly liberates the programmer’s energy and eliminates the fear of releasing memory like traditional languages. However, even with the automatic release of GC, it doesn’t mean that memory leaks don’t exist. Memory leaks are still a problem developers can’t get around.


GC in Node.js

Node.js uses V8 as the Javascript execution engine, so talking about node.js GC is equivalent to talking about V8 GC. In V8, the memory release of an object is freed to see if there is any place in the program to hold a reference to the object. In V8, references to root objects (window in browser, global in Node.js) are sorted through each GC, and V8 marks them as reachable if they can be accessed from root’s reference chain, and unreachable if they can’t. Objects marked as unreachable (that is, objects without references) are reclaimed by V8. The cause of memory leaks in NodeJS is that objects that should have been cleaned, referenced by reachable objects, are not cleaned properly and remain in memory.


Several cases of memory leaks

Global variables

a = 10; // No object is declared
global.b = 11; // Global variable references
Copy the code

The reason for this is simple: global variables hang directly on the root object and are not cleaned up.


Second, the closure

function out(){
    const data = Buffer.alloc(100);
    inner = function(){
        void data
    }
}
Copy the code

Closures refer to variables in parent functions, and if the closure is not released, it can cause a memory leak. The example here is simply hanging a reference to a global object, while the actual business situation is caused by hanging a reference to an object that can be traced back to root.


3. Event monitoring

NodeJS event listeners can also leak memory. For example, if you listen to the same event repeatedly and forget to removeListener, memory leaks will occur. This can easily happen when you add events to a reusable object. For example, if the keepAlive value of the Agent in NodeJS is true, memory leakage may occur. When Agent keepAlive is true, the socket used before will be reused. If you add event listening to the socket and forget to clear it, the reuse of the socket will lead to repeated event listening, resulting in memory leakage. The principle is the same as adding event listener forgot to clear. When using HTTP modules using NodeJS, it is not a problem to reuse them through keepAlive, which can cause memory leaks. So you need to understand the life cycle of adding event listeners and be careful to remove them yourself.


Other reasons

There are other situations that can cause memory leaks, such as caching. When using the cache, you need to know how many objects are in the cache. If there are too many objects in the cache, you need to limit the maximum number of objects in the cache. Also, cpu-heavy code can cause memory leaks. If the server is running with high CPU synchronous code, NodeJS is single-threaded, so it can’t handle subsequent requests, resulting in high memory usage.


How to avoid memory leaks

  • ESLint checks code for unexpected global variables.
  • When using a closure, you need to know what objects the closure contains and when objects referencing the closure are cleared. It is best to avoid writing complex closures, because memory leaks caused by duplicate closures are hard to see without printing a memory snapshot.
  • When binding events, be sure to clear them at the appropriate time. When writing a class, it is recommended to use the init function to bind and request resources for the class’s event listeners, and then the destory function to release the events and occupied resources.