Contenido

Solución definita a los memory leaks de IE6

10 jun

+ 4

Cuando hablamos de Memory Leak (Fuga de memoria, no me gusta nada como suena), nos referimos a los problemas que un programa tiene para gestionar la memoria. En este caso, Internet Explorer 6 es un experto y siempre lo ha demostrado con famoso cuelgues y pantallazos azules que tan felices nos hacía. :D

Uno de los mayores problemas con los que nos encontramos es que al crear objetos DOM, Internet Explorer 6 consume una memoria que al eliminar el elemento no termina de liberar. Douglas Crockford hace unos años comentaba este problema y nos ponía un ejemplo para ver el resultado en nuestras propias carnes.

Veamos un ejemplo que nos provoca este problema con el consumo de memoria:

/*global setTimeout */
            (function (limit, delay) {
                var queue = new Array(10);
                var n = 0;

                function makeSpan(n) {
                    var s = document.createElement('span');
                    document.body.appendChild(s);
                    var t = document.createTextNode(' ' + n);
                    s.appendChild(t);
                    s.onclick = function (e) {
                        s.style.backgroundColor = 'red';
                        alert(n);
                    };
                    return s;
                }

                function process(n) {
                    queue.push(makeSpan(n));
                    var s = queue.shift();
                    if (s) {
                        s.parentNode.removeChild(s);
                    }
                }

                function loop() {
                    if (n < limit) {
                        process(n);
                        n += 1;
                        setTimeout(loop, delay);
                    }
                }

                loop();
            })(10000, 10);

Como podemos ver si destripamos el código anterior, vemos como beneramos 10000 elementos <span /> y posteriormente los borramos. Si usamos la lógica al ejecutar este código, el consumo de memoria (Podemos verlo en Windows en Administrador de tareas de Windows > Uso de PF) se dispararía y despues volvería a su normalidad al eliminar los elementos, en Internet Explorer esto no ocurre así, cuando lanzamos el script este usa la memoria en crear los elementos, pero despues esta no se libera. Ahí el problema.

¿Como solucionarlo?

Veamos la función que devuelve el objeto que es la que ocasiona el problema.

 function createButton() {
      var obj = document.createElement("button");
      obj.innerHTML = "click me";
      obj.onclick = function() {
        //handle onclick
      }
      obj.onmouseover = function() {
        //handle onmouseover
      }
      return obj;//return a object which has memory leak problem in IE6
    }

    var dButton = document.getElementsById("d1").appendChild(createButton());
    //skipped....

En este punto estamos devolviendo la dirección de memoria en la que se encuentra el objeto que acabamos de crear, en Internet Explorer esta porción de memoria nunca termina de liberarse al eliminar el objeto.

function createButton() {
      var obj = document.createElement("button");
      obj.innerHTML = "click me";
      obj.onclick = function() {
        //handle onclick
      }
      obj.onmouseover = function() {
        //handle onmouseover
      }

      //this helps to fix the memory leak issue
      try {
        return obj;

      } finally {
        obj = null;
      }
    }

    var dButton = document.getElementsById("d1").appendChild(createButton());
    //skipped....

Gracias a try{} podemos lanzar finally{} que se ejecutará siempre despues de la ejecución, incluso despues del return de nuestras funciones, de esta forma vaciamos la porción de memoria y evitamos que posteriormente se quede inutilizada. Por otro lado, al estar devolviendo el objeto mediante el return, lo tenemos disponible como parámetro de la función padre que llame a esta.

Más información y demos

  • Como era eso… “un pequeño paso para el hombre, un gran paso para la humanidad” :-D

  • interesante… pero creo que no hay para que hacerles la vida mas fácil a los usuarios de IE6; navegador con el que nosotros (web developers) tenemos que lidiar a diario con otros miles de fixes para que todo funcione como debe.
    En mi opinión, más fixes retrasa una actualización completamente necesaria y urgente, para quienes les gusta el navegador de m*******t.
    Salu2!

  • Andrés, tras un rato analizando el primer ejemplo no he podido encontrar ningún ciclo entre objetos DOM (COM) y objetos nativos: tal como está no hay perdidas de memoria en IE6.

    Visto el código del ejemplo de Crockford me parece que a la hora de reproducirlo se ha quedado atrás un fragmento que es el que liga el objeto de activación (nativo) con el objeto span DOM:

    function makeSpan(n) {
    var s = document.createElement(‘span’);
    document.body.appendChild(s);
    var t = document.createTextNode(‘ ‘ + n);
    s.appendChild(t);

    s.onclick = function (e) {

    };

    return s;
    }

    Por otra parte, me parece que el patrón propuesto sólo es útil en aquellos casos en los que es necesario devolver una referencia al objeto DOM mediante un return. Si este no fuera el caso la estructura finally es innecesaria. Creo que sería conveniente una aclaración sobre esto ya que el título del post induce confunde un poco.

    Un saludo de un fiel lector.
    Jose.

Comentar

#

Me reservo el derecho de eliminar y/o modificar los comentarios que contengan lenguaje inapropiado, spam u otras conductas no apropiadas en una comunidad civilizada. Si tu comentario no aparece, puede ser que akismet lo haya capturado, cada día lo reviso y lo coloco en su lugar. Siento las molestias.