1

I'm working on a number of games (here's one: https://arcade.ly/games/asteroids/) and, to try to keep the framerate a little more reliable on some browsers/devices, I'm using object pools for frequently created objects, like particles. I create hundreds, and sometimes thousands of particles every second, so reusing them should in theory stabilise memory usage and lead to fewer issues due to GC pauses. This is really only noticeable as an issue on mobile devices, but anyway.

I've written a fairly basic object pool implementation, which uses a singly linked list of objects to manage the pool (I wanted to avoid allocations due to array resizing):

(function () {
    'use strict';

    pinjector
        .module('starCastle')
        .factory(
            'objectPool',
            [
                objectPool
            ]);

    function objectPool() {
        var service = {
            createPool: createPool
        };

        return service;

        function createPool(objectConstructor) {
            var nextObjectToUse = null,
                firstLiveObject = null,
                liveObjectCount = 0;

            var pool = {
                create: create,
                release: release,
                getLiveObjectCount: getLiveObjectCount
            };

            return pool;

            function create() {
                var returnValue;

                if (nextObjectToUse) {
                    returnValue = nextObjectToUse;
                    nextObjectToUse = nextObjectToUse.__next;
                    returnValue.isNew = true;
                } else {
                    returnValue = objectConstructor();
                    returnValue.isNew = true;
                }

                returnValue.__next = undefined;
                ++liveObjectCount;
                return returnValue;
            }

            function release(object) {
                if (!object) {
                    return;
                }

                object.__next = nextObjectToUse;
                nextObjectToUse = object;
                --liveObjectCount;
            }

            function getLiveObjectCount() {
                return liveObjectCount;
            }
        }
    }
}());

(Aside: the reason this looks a bit like an Angular 1.x service/factory is because that's what it used to be. Long, boring story behind that.)

Here's where it gets weird. When I collect an Allocation Profile using Chrome Dev Tools it tells me that most of the memory allocation is occurring in the release(object) function above. If I play the game for a minute or two whilst collecting the profile I'll see several MB allocated in this function, but I don't understand why.

However, if I comment out the following lines of code the memory allocation disappears, and doesn't reappear anywhere else in the profile:

// Commenting out these two lines of code causes allocation to disappear
// object.__next = nextObjectToUse;
// nextObjectToUse = object;

The question is, why?

Basically "all" these lines are doing is some pointer juggling to add an object to the head of the list. I'm staring at this and assuming I've done something dumb: e.g., I'm wondering if I've somehow got the juggling wrong (or likewise if I've got the juggling wrong in the create() function), but I can't quite see how.

So can anybody here see why this is resulting in so much memory allocation?

(This is Chrome 58 on OSX, btw.)

Thanks,

Bart

Bart Read
  • 2,717
  • 3
  • 22
  • 32

0 Answers0