La gestión de memoria en lenguajes como Javascript, los cuales están basados en "Recolectores de Basura" (Garbage Collector) escapa en gran parte al control del programador, ya que es el propio mecanismo del Garbage Collector el que decide en que momento ha de ejecutarse para compactar la memoria.

Este proceso automatizado, en principio no supone ningún problema, puesto que por lo general es una gestión imperceptible para el usuario final, pero hay ciertos casos donde puede ser útil tener más control de la memoria. Como por ejemplo, en aplicaciones donde se usen algunos objetos que son muy costosos de instanciar, o en el desarrollo de videojuegos donde se destruyen y creen constantemente actores, objetos de calculo, etcétera, provocando caídas en los FPS (frames por segundo) cuando el Garbage Collector trabaja.

El patrón de diseño Object Pool puede sernos de utilidad en estos casos, ya que nos permite "cierto" control de la memoria evitando que los objetos se destruyan, y por lo tanto evitando darle trabajo al Garbage Collector.

Básicamente, un Object Pool no es otra cosa que una lista de "instancias" de un mismo objeto, el cual nos permite reciclar continuamente estas instancias. La manera de hacer esto es sencilla, cuando necesitamos una instancia de nuestro objeto, se la pedimos al Pool, el cual, cogería la primera de su lista si tuviera, o crearía una nueva instacia si no tuviera ninguna almacenada, una vez entregada, elimina la referencia de su lista. Cuando nuestra aplicación ha terminado con esta intancia, no la destruimos, sino que la devolvemos al Pool, de manera que esta podrá ser reutilizada la próxima vez que se pida una nueva instancia.

Hay muchas formas de crear y gestionar un Object Pool, pero me gustaría compartir un pequeño modulo que ya hace todo el trabajo para nosotros.

Si estamos usando Node.js, Browserify, o Webpack podemos instalarlo desde NPM:

npm install --save obj-pool

Y requerirlo así:

var ObjPool = require('obj-pool');

También podemos añadir directamente el script en nuestro HTML, haciendo que ObjPool sea añadido a window:

if(ObjPool)console.log('todo ok!');

Su uso es bastante simple, tan solo debemos de instanciar nuestro Pool pasandole el constructor del objeto que queremos almacenar:

var myPool = new ObjPool( MyObject );

De esta forma, el pool creará las instancias según las demandemos con el método .alloc():

var myInstance = myPool.alloc(); 
//Hago lo que quiera con myInstance

Y una vez hemos terminado con la instancia en cuestión, la devolvemos al Pool con .free() y eliminamos la referencia de nuestro código:

myPool.free(myInstance);
myInstance = null;

//Si el scope es local, no haría falta eliminar la referencia

Simple ¿verdad?, ahora pongamos el caso de que no quieres que las instancias se creen bajo demanda, puesto que pueden ser muchas y muy costosas de instanciar, y prefieres controlar el momento de su creación, para esto podemos pasarle como segundo parámetro a ObjPool { amount: N }, de esta forma se crearán N instancias de nuestro objeto al crear el Pool:

//Crea un pool con 100 instancias de MyObject
var myPool = new ObjPool( MyObject, { amount: 100});

De esta forma tendremos 100 instancias a nuestra disposición al crear el pool, y en caso de que todas se usen, se empezarían a crear nuevas bajo demanda. Si por el contrario, no queremos que se instancien de golpe en la creación del pool, y preferimos otro momento, podemos usar el método .generate( amount ) para gestionarlo:

var myPool = new ObjPool( MyObject );
//Hago cosas
myPool.generate(100); // Genero 100 nuevas instancias que se almacenan en el pool

Pongamos ahora que queremos crear un Pool de un objeto al que necesitamos pasarle parámetros al instanciarlo, para ello podemos indicarle como segundo parámetro al ObjPool { args : [params...]}, usemos por ejemplo la clase Audio( src ):

//Creamos un pool de Audios con './assets/audio.mp3' como src
var myPool = new ObjPool( Audio, {args: ['./assets/audio.mp3')]});

Podemos pasarle todos los parámetros que queramos a nuestro objeto separando estos con comas (,) dentro del array de args.

Además podremos comprobar en todo momento cuantas instancias tiene el pool mediante .length:

if(myPool.length === 0)console.log('vacío');

Y cuando terminemos, podremos eliminar la lista de instancias con .clear(), dandole así nuevamente trabajo al Garbage Collector. En el caso de un juego, podríamos aprovechar cargas entre niveles, o momentos similares para limpiar los pools si fuera necesario, momentos donde algún que otro parón no sea molesto para el usuario.

También en algún momento podríamos necesitar de realizar acciones cuando el pool nos entrega la instancia y cuando la devolvemos de vuelta al pool, acciones como inicializar y resetear el objeto por ejemplo. Para estos casos, el pool buscará los métodos .init() y .reset() del objeto que hemos usado, y los ejecutará si estos existieran:

function MyEntity(){
}

MyEntity.prototype.constructor = MyEntity;

MyEntity.prototype.init = function(){
    //Hacemos cualquier acción al salir del pool
}

MyEntity.prototype.reset = function(){
  //Hacemos cualquier acción al volver al pool
}

var pool = new ObjPool(MyEntity);
var entity = pool.alloc(); //Ejecutará .init();
pool.free(entity); //Ejecutará .reset();

Para terminar os dejo el enlace al modulo den Github, donde podréis consultar la documentación, mejorarlo, etcétera. Espero que os sea de utilidad!

ObjPool en Github: https://github.com/Nazariglez/obj-pool