Search code examples
javascriptgarbage-collectiongarbage

Web Worker Creating Garbage


I'm working on a project that utilizes web workers. It seems that the workers are generating quite a bit of extra garbage that has to be collected from the message passing.

I'm sending three things to the worker via post message from the main thread. First is just a number, second is an array with 7 numbers, and 3rd is the date. The firs two are properties of an object as seen below. This is called every 16ms on RAF for about 20 objects. The GC ends up collecting 12MB every 2 seconds or so. I'm wondering if there is a way to do this without creating so much garbage? Thanks for any help!

        //planet num (property of object) is just a number like: 1

        //planetele looks like this (property of an object)
        //[19.22942, 313.4868, 0.04441, 0.7726, 170.5310, 73.9893, 84.3234] 

        //date is just the date object

        //posted to worker like so:

        planetWorker.postMessage({ 
            "planetnum": planet.num,
            "planetele": planet.ele,
            "date": datet
        });

        //the worker.js file uses that information to do calculations 
        //and sends back the planet number, with xyz coordinates. (4 numbers)

        postMessage({data: {planetnum : planetnum, planetpos: planetpos}});

Solution

  • I tried two different avenues and ended up using a combination of them. First, before I sent some of the elements over I used JSON.stringify to convert them to strings, then JSON.parse to get them back once they were sent to the worker. For the array I ended up using transferable objects. Here is a simplified example of what I did:

    var ast = [];
    
    ast.elements = new Float64Array([0.3871, 252.2507, 0.20563, 7.005, 77.4548, 48.3305, 0.2408]);
    ast.num = 1;
    
    var astnumJ = JSON.stringify(ast.num); // Probably not needed, just example
    
    // From main thread, post message to worker
    asteroidWorker.postMessage({ 
        "asteroidnum": astnumJ,
        "asteroidele": ast.elements.buffer
    },[ast.elements.buffer]);
    

    This sends the array to the worker, it doesn't copy it, which reduces the garbage made. It is now not accessible in the main thread, so once the worker posts the message, you have to send the array back to the main thread or it wont be accessible as a property of ast anymore. In my case, because I have 20 - 30 ast objects, I need to make sure they all have their elements restored via post message before I call another update to them. I did this with a simple counter in a loop.

    // In worker.js 
    asteroidele = new Float64Array(e.data.asteroidele); // cast to type
    asteroidnum = JSON.parse(e.data.asteroidnum); // parse JSON
    
    // Do calculations with this information in worker then return it to the main thread
    
    // Post message from worker back to main
    self.postMessage({
        asteroidnum : asteroidnum, 
        asteroidpos : asteroidpos, // Calculated position with elements
        asteroidele : asteroidele // Return the elements buffer back to main
    });
    
    // Main thread worker onmessage function
    asteroidWorker.onmessage = function(e){  
        var data1 = e.data;
        ast.ele = data1.asteroidele; // Restore elements back to ast object
    }
    

    Not sure this is the best approach yet, but it does work for sending the array to and from the worker without making a bunch of extra garbage. I think the best approach here will be to send the array to the worker and leave it there, then just return updated positions. Working on that still.