DEV Community

Cover image for Best Practices to Avoid Memory Leaks
Amod Shinde
Amod Shinde

Posted on

Best Practices to Avoid Memory Leaks

In the Previous Blog we understood the Memory management in Node.js, how Garbage Collector (GC) works and what are the possible causes of memory leaks despite GC playing a crucial role. In this blog, let's look at best practices to ensure efficient memory use in Node.js.

REDUCE USE OF GLOBAL VARIABLES

Since global variables are never garbage collected, it's best to ensure you don't overuse them.

In javascript particularly you need to keep in mind certain aspects to reduce global variables

  1. Avoid accidental globals

In Javascript, if you assign a value to an undeclared variable, Javascript automatically hoists it as a global variable in default mode. Also, this goes the same with the word 'this' used in functions in the global scope.

Examples:


function apple() {
    red = true; // this red variable gets hoisted in global space
}

function mango() {
// since 'this' in global functions refers to global this varible is hoisted in global space
this.type = "Alphanso";  
}
Enter fullscreen mode Exit fullscreen mode

Solution: It is helpful to write javascript in a strict mode with 'use strict' annotation at the top of the JS file. In newer versions of Node.js you can globally enable strict mode by passing '- - use_strict ' flag when running node command.

'use strict'

function apple() {
    red = true; // this will throw an error
}

function mango() {
// this function will have its own scope for 'this'
this.type = "Alphanso";  
}
Enter fullscreen mode Exit fullscreen mode

Caution: Be careful when you use arrow functions though, because even in use strict mode 'this' in arrow function will be in global space

// This will also become a global variable as arrow functions
const plantation = () => {
    this.coconut = "Brown";
}
Enter fullscreen mode Exit fullscreen mode

solution: use the no-invalid-this rule from ESLint to avoid such cases.

Use global scope wisely :

  • As much as possible don't use the global scope, make the best use of the local scope inside the functions, as that will be garbage collected and will keep memory free.
  • Try to define only constants, cache and reusable variables in global. Mark variables as null whenever the values are not needed.
  • Do not use global space as a medium to pass values from one function to other, use function parameters instead.
  • Dont store big objects in the global scope. If you have to store them, then nullify them when not needed. Dont let cache objects grow indefinitely, cleanup once and while.
  1. Use Stack Memory Effectively

    Accessing stack is much faster than accessing heap memory, hence try to use stack variables more frequently than their counterparts. This also ensures we don't accidentally cause memory leaks. Ofcourse in a real-world scenario, it's impossible to create a usable application by only using static data. But we can follow some tricks to make better use of the stack.

    1. Avoid heap object references from stack variables when possible. Also, don’t keep unused variables.
    2. Destructure and use fields needed from an object or array rather than passing around entire objects/arrays to functions. This avoids keeping a reference to objects inside closures.
    function outer() {
        const obj = {
            foo: 1,
            bar: "hello",
        };
    
        const closure = () {
            const { foo } = obj;
            myFunc(foo);
        }
    }
    
    function myFunc(foo) {}
    
    1. Use Heap Memory Effectively

    In real-world applications it is quite impossible to completely avoid using heap memory but we can make it more efficient by following a few tips:

    1. copy objects instead of referencing them. Pass the reference only if the object is huge and copy operation is expensive.
    2. Avoid object mutations as much as possible, instead use object spread or object.assign and create a copy.
    3. Avoid creating multiple references to the same object
    4. Avoid creating huge object trees else keep them short-lived

    Use APM to track your heap memory usage.

    1. Be Cautious when using Closures, Timers & Event Handlers

    For timers always remember to pass copies of objects to avoid mutations and clear timers when done using clearTimeout and clearInterval methods.

    Also, clear listeners once the job is done, don't keep them running forever. In cases where they are holding on to object reference from the parent scope.

Originally posted at amodshinde.com

Discussion (1)

Collapse
nicolaslima321 profile image
Nicolas Lima

That will help me a lot with problems, that I dealed with, in a Electron application!! Thank you so much!!