skip to Main Content

For efficient memory usage of a highly used class instance (multiple calls to init), I would like to know if memory for anything inside init function is allocated if it returns early.

Sandbox

class X {
    private static smth: Smth

    public static init(): Smth {
        // if this is evalueated to true and returned
        if (this.smth) {
            return this.smth;
        }

        // does memory allocated here (imagine there will be a big object)?
        const smth: Smth = {
            x: 1
        }

        this.smth = smth

        return this.smth
    }

    // if for previous question answer is yes, can we avoid memory allocation for `const smth` like this?
    public static getInstance(): Smth {
        if (!this.smth) {
            throw new Error("Not initialized");
        }

        return this.init();
    }
}

type Smth = {
    x: number
}

2

Answers


  1. Brief Answer: No

    The code will not allocate memory for the variables and objects in the early-return case.

    You can verify this in several ways. One of the simplest methods is to assign a large object to smth.

    Example Code (Run this in the browser console):

    class X {
        static smth = null;
    
        static init() {
            if (this.smth) {
                console.log('Returning existing smth');
                return this.smth;
            }
    
            const bigObject = new Array(50000000).fill('a');
    
            const smth = {
                x: 1,
                bigData: bigObject,
            };
    
            this.smth = smth;
            return this.smth;
        }
    
        static getInstance() {
            if (!this.smth) {
                throw new Error('Not initialized');
            }
            return this.init();
        }
    }
    
    const time1 = new Date().getTime();
    const smth1 = X.init();
    const time2 = new Date().getTime();
    const smth2 = X.init();
    const time3 = new Date().getTime();
    
    console.log("Time for first init:", time2 - time1); // On my computer: 8948
    console.log("Time for second init:", time3 - time2); // 50
    

    Observation:

    • The time for the second initialization is significantly lower than the first one.
    • If you open the Memory tab in Chrome DevTools, take a snapshot before and after the second initialization, you will notice that the memory usage does not change.

    Node.js Compatibility:

    This behavior also works in Node.js (tested on my computer with Node.js 20.12.2):

    Time for first init: 10480
    Time for second init: 7
    
    Login or Signup to reply.
  2. The memory for the variable const smth (which is hoisted) is typically allocated when the function is called, as part of the stack space for that function call. (An engine might be able to optimise this away, but it’s hardly worth it – the stack allocation and later deallocation is cheap).

    The memory for the "large" object isn’t allocated until the object is actually created, i.e. when the expression with the { … } object literal is evaluated.

    If for previous question answer is yes, can we avoid memory allocation for const smth like this?

    You might be able, yes, but at the cost of an extra function call. The overhead of the stack allocation(s) for a function call is much larger than a single (pointer) variable, so this is unlikely to gain anything.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search