viernes, 2 de septiembre de 2011

A recoger la basura

Este post surgió por una consulta en mi trabajo asi que Gracias Guille por la idea!

Vamos a hablar un poco del famosisimo proceso de Garbage colection de javascript
Como sabrán (deberían) el Garbage colector de un lenguage es el encargado de liberar la memoria que ya no se está utilizando, pero... como es que hace esto?
La parte dificil aqui es decidir cual es la memoria que se debe liberar y cual no, entonces es necesario un algoritmo para marcar que objetos en memoria no son más necesarios.
En las primeras implementaciones (javascript 1.1 Netscape 3) el garbage collector utilizaba un mecanismo llamado
Reference Counting (Conteo de referencias) el cual basicamente mantiene un registro de la cantidad de variables que apuntan a un objeto en memoria.


var a = {};
//creamos un objeto en memoria y lo apuntamos desde la variable a
// la cantidad de variables (referencias al objeto) es 1

var b=a;
//con b apuntamos al mismo objeto aumentando su reference counting en 1
//por lo tanto ahora 2 variables le apuntan

b=null
//desapuntamos b, decrementa el conteo de referencias nuevamente a 1

a=null
//desapuntamos el objeto y su conteo de referencias cae a 0
// ahora es un objeto en memoria no apuntado por nadie propenso
// a la recolección de basura (y recupero del espacio en memoria)

Sin embargo el algoritmo de reference counting, tiene un problema Gravisimo
las referencias ciclicas
esto es

var a={color:'rosa'};
var b={color:'rojo'};
var c={color:'negro'};
//creo 3 objetos y los apunto con 3 variables
//cada objeto tiene su reference count en 1
//RC(rosa):1 RC(rojo):1 RC(negro):1


a.next=b;
//ahora apunto con a.next al objeto rojo
//RC(rosa):1 RC(rojo):2 RC(negro):1
b.next=c;
//con b.next apunto al objeto negro
//RC(rosa):1 RC(rojo):2 RC(negro):2
c.next=b;
//con c.next apunto nuevamente al objeto rojo
//RC(rosa):1 RC(rojo):3 RC(negro):2

//desapunto todas las variables
a=null;
b=null;
c=null;
//decrementando en 1 todos los RC
//RC(rosa):0 RC(rojo):2 RC(negro):1
//cuando el proceso de garbage colector note que el objeto
//rosa no es apuntado por nadie lo eliminará, eliminando
//el puntero next al objeto rojo decrementando su RC nuevamente
//RC(rosa):0 RC(rojo):1 RC(negro):1
//pero rojo y negro continuan apuntandose entre si por tanto
//sus RC jamas decaen a 0 y no son liberados
//MEMORY LEAK!!!!!


Los objetos apuntados ciclicamente no pueden ser liberados mediante este algoritmo, por lo cual muchos nisiquiera aceptan a Reference Counting como un algoritmo de Garbage Collection.

Por suerte los tiempos cambiaron y la mayoria de los interpretes de javascript han decidido actualizar sus algoritmos de garbage collection a algo mucho mas efectivo llamado

Mark-and-sweep

Que basicamente toma un contexto y revisa para todas las variables de ese contexto, todos los objetos en memoria alanzables desde esas variables y los marcan como "ALCANZABLE" desde el contexto del programa.
luego recorre la memoria en busca de los objetos no marcados como "ALCANZABLES" y los borra.

es un algoritmo que requiere mucho mas trabajo pero es más efectivo.

tienen una mejor explicación aqui

como pueden ver en
Google chrome utiliza garbage collection por Mark-and-sweep
"he V8 garbage collector reclaims memory used by objects that can never again be accessed."
y como lo hace en

Linda historia... pero es "historia"?

En cuanto al lenguage si, PEEEERO y este es un gran pero, dependiendo del entorno en el que uses javascript esto puede variar.

Para los navegadores?

Lamentablemente no, todos los navegadores aún continuan utilizando Reference Counting para los objetos del DOM
por lo tanto basta apuntar a un objeto del DOM desde javascript y al objeto javascript desde el DOM y habremos generado un memory leak

veamos un ejemplo


var tryingToLeak = function(){
obj=document.getElementById("btnClearLog");
//apunto al DOM desde JS
document.getElementById("btnClearLog").expandoProperty=obj;
//apunto a JS desde el DOM
obj.bigString=new Array(10000).join(new Array(2000).join("XXXXX"));
//Consumo memoria en JS
};
tryingToLeak();


cada vez que ejecutamos el codigo anterior el proceso reclama 200mb de ram que no devuelve jamas
pueden probarlo utilizando el "Administrador de tareas de Google Chrome"
por mas que la variable de JS ya no tenga visibilidad el objeto del DOM continua con un RC mayor a 1 y por tanto no es sujeto a ser eliminado.

A continuación les dejo dos códigos que demuestran que no se producen memory leaks en javascript


var tryingToLeak = function(){
for(var i=0; i<=1000; i++){
var str=new Array(1000).join(new Array(2000).join("XXXXX"));
}
};
tryingToLeak();


var tryingToLeak = function(){
for(var i=0; i<=1000; i++){
var arr=[];
for(var j=0; j<=1000;j++){
arr.push(new Array(2000).join('XXXXX'));
}
}
};
tryingToLeak();


Saludos

No hay comentarios:

Publicar un comentario