WarsawJS Meetup: Debugging memory leaks

We talk about JavaScript. Each month in Warsaw, Poland.

Speaker

Maciej Gołaszewski

"WarsawJS Meetup: Debugging memory leaks" [EN]

2019-12-12

@jodator

Memory management in JavaScript

JavaScript automatically allocates memory when objects are created.

Garbage collection - is a process of automatically freeing memory when those objects are no longer used.

Memory life-cycle:

Step 1: Allocate

Step 2: Use

Source giphy.

Step 3: Release

The Garbage collector finds the memory which is no longer need.

All modern browsers uses the "mark-and-sweep" algorithm to detect objects that are no longer needed by finding "unreachable objects".

Source Google Tools for Web Developers: Memory Terminology.

As of 2019 We cannot release memory manually (call the garbage collector).

Common causes of memory leaks

But only if those are not needed by the application at given time. Otherwise those are valid objects.

Available Tools

Using the tools - preparation

Run your browser with all features striped down.

            
                google-chrome \
                    --disable-extensions \
                    --disable-plugins \
                    --incognito
                    http://example.com
            
        

Firefox dev tools

Chrome dev tools

Chrome dev tools - allocation timeline

Chrome dev tools - comparing snapshots

Live debugging

Source giphy.

Example: Accidental global (Demo)

            
                document.getElementById( 'ml-global' )
                    .addEventListener( 'click', () => {
                        // window.bo = new BigObject();
                        // bo = new BigObject();
                        this.bo = new BigObject(); // this === window
                    } );
            
        

Example: Intermediate values (Demo)

            
                const config = { bo: new BigObject(), foo: 'bar',
                    interval: 1000 };

                logSomething( config );

                function logSomething( config ) {
                    setInterval( () => { // Also: calback!
                        console.log( config.foo );
                    }, config.interval );
                }
            
        

Example: Shared lexical scope (Demo) from An interesting kind of JavaScript memory leak.

            
                let theThing = null;
                function replaceThing() {
                    const originalThing = theThing;
                    function unused() {
                        if ( originalThing ) {
                            // ...
                        }
                    }
                    theThing = {
                        bo: new BigObject(),
                        someMethod: function() {
                            // ...
                        }
                    };
                }
            
        

Example: Forgotten stuff (Demo)

            
                const bo = new BigObject();
                bo.node = document.getElementById( 'a-div' )

                function detachNode() {
                    document.getElementById( 'a-div' ).remove(); // Removes from the DOM
                }

                setInterval( () => { // Interval is not cleared
                    console.log( bo[ 3 ] );
                }, 500 );

                console.log( bo ); // bo resides in console GC root
            
        

ProTips™

Thanks!