tipo objetos multiple msd memoria limpiar leak herencia heredar estructuras datos dato chrome javascript memory-leaks cross-browser closures

javascript - objetos - limpiar memoria jquery



Riesgo de pérdida de memoria en cierres de JavaScript (2)

Bien, después de pasar algo de tiempo con esa herramienta IEJSLeaksDetector , descubrí que las cosas de las que hablé en mi pregunta original NO CAUSAN MEM LEAKS . Sin embargo, hubo una fuga que apareció. Afortunadamente, he logrado encontrar una solución:

Tengo un guión principal, y en la parte inferior, hay una vieja escuela:

window.onload = function() { //do some stuff, get URI, and call: this[''_init'' + uri[0].ucFirst()](uri);//calls func like _initController //ucFirst is an augmentation of the String.prototype }

Esto provoca una fuga en IE8, que no pude solucionar con una window.onbeforeunload . Antes de window.onbeforeunload controlador. Parece que debes evitar unir el manejador al objeto global. La solución radica en cierres y oyentes de eventos, es un poco faff, pero esto es lo que terminé haciendo:

(function(go) {//use closure to avoid leaking issue in IE function loader() { var uri = location.href.split(location.host)[1].split(''/''); //do stuff if (this[''_init'' + uri[0].ucFirst()] instanceof Function) { this[''_init'' + uri[0].ucFirst()](uri); } if (!(this.removeEventListener)) { this.detachEvent(''onload'',loader);//(fix leak? return null; } this.removeEventListener(''load'',loader,false); } if (!(go.addEventListener)) { go.attachEvent(''onload'',loader);//(IE... } else { go.addEventListener(''load'',loader,false); } })(window);

De esta forma, el evento de carga (on) se libera justo antes de que regrese el manejador window.load , según la herramienta IEJSLeaksDetector, NO hay fugas en mi aplicación. Estoy feliz con eso. Espero que este fragmento sea útil para uno de ustedes; si alguien tiene sugerencias para mejorar este enfoque, ¡no dude en hacerlo!

Saludos, y gracias a todos ustedes que se tomaron la molestia de leer y probar mi drible arriba!

PD: en caso de que a alguien le importe, aquí está el método ucFirst String:

if (!(String.prototype.ucFirst)) { String.prototype.ucFirst = function() { "use strict"; return this.charAt(0).toUpperCase() + this.slice(1); }; }

Resuelto

Hay mucha información contradictoria en la web sobre este tema. Gracias a @John, me las arreglé para pensar que los cierres (como se usan a continuación) no son la causa de las pérdidas de memoria, y que, incluso en IE8, no son tan comunes como dicen las personas. De hecho, solo hubo una fuga en mi código, que no resultó tan difícil de solucionar.

A partir de ahora, mi respuesta a esta pregunta será:
AFAIK, la única vez que IE8 pierde, es cuando se adjuntan eventos / los manejadores se configuran en el objeto global. ( window.onload , window.onbeforeunload , ...). Para evitar esto, vea mi respuesta a continuación.

ENORME ACTUALIZACIÓN:

Ahora estoy completamente perdido ... Después de algún tiempo hurgando en artículos y tuts tanto antiguos como nuevos, me queda al menos una contradicción enorme. Mientras que uno de los gurús de JavaScript (Douglas Crockford) dice:

Como IE no puede hacer su trabajo y reclamar los ciclos, nos corresponde a nosotros hacerlo. Si rompemos los ciclos de manera explícita, IE podrá reclamar la memoria. Según Microsoft, los cierres son la causa de pérdidas de memoria. Esto, por supuesto, está muy mal, pero lleva a Microsoft a dar muy malos consejos a los programadores sobre cómo lidiar con los errores de Microsoft. Resulta que es fácil romper los ciclos en el lado DOM. Es prácticamente imposible romperlos en el lado de JScript.

Y como @freakish señaló que mis fragmentos a continuación son similares al funcionamiento interno de jQuery, me sentí bastante seguro de que mi solución no causaba pérdidas de memoria. Al mismo tiempo, encontré esta página MSDN , donde la sección Circular References with Closures me interesó especialmente. La siguiente figura es más o menos una representación esquemática de cómo funciona mi código, ¿no es así?

La única diferencia es que tengo el sentido común de no unir mis oyentes de eventos a los elementos mismos.
Todo lo mismo Douggie es bastante inequívoco: los cierres no son la fuente de mem-leaks en IE. Esta contradicción no me deja ni idea de quién tiene la razón.

También descubrí que el problema de la fuga no está completamente resuelto en IE9 (no puedo encontrar el enlace ATM).

Una última cosa : también aprendí que IE maneja el DOM fuera del motor de JScript, lo que me molesta cuando cambio los elementos secundarios de un elemento <select> , basado en una solicitud de ajax:

function changeSeason(e) { var xhr,sendVal,targetID; e = e || window.event;//(IE... targetID = this.id.replace(/commonSourceFragment/,''commonTargetFragment'');//fooHomeSelect -> barHomeSelect sendVal = this.options[this.selectedIndex].innerHTML.trim().substring(0,1); xhr = prepareAjax(false,(function(t) { return function() { reusableCallback.apply(this,[t]); } })(document.getElementById(targetID)),''/index/ajax''); xhr({data:{newSelect:sendVal}}); } function reusableCallback(elem) { if (this.readyState === 4 && this.status === 200) { var data = JSON.parse(this.responseText); elem.innerHTML = ''<option>'' + data.theArray.join(''</option><option>'') + ''</option>''; } }

Si IE realmente administra el DOM como si el motor de JScript no estuviera allí, ¿cuáles son las probabilidades de que los elementos de la opción no se desasignaran utilizando este código?
Deliberadamente agregué este fragmento como ejemplo, porque en este caso paso variables que forman parte del ámbito de cierre como argumento para una función global. No pude encontrar ninguna documentación sobre esta práctica, pero en base a la documentación provista por Miscrosoft, debería romper cualquier referencia circular que pueda ocurrir, ¿no es así?

Advertencia : pregunta larga ... (lo siento )

He escrito un par de JavaScripts bastante grandes para hacer llamadas Ajax en mi aplicación web. para evitar toneladas de devoluciones de llamadas y eventos, aprovecho al máximo la delegación de eventos y los cierres. Ahora he escrito una función que me hace pensar en posibles fugas de memoria. Aunque sé que IE> 8 se ocupa de los cierres mucho mejor que sus predecesores, la política de la compañía es apoyar IE 8 de todos modos.

A continuación he proporcionado un ejemplo de lo que estoy hablando, here puede encontrar un ejemplo similar, aunque no utiliza ajax, sino un setTimeout, el resultado es prácticamente el mismo. (Por supuesto, puede omitir el código a continuación, a la pregunta en sí)

El código que tengo en mente es este:

function prepareAjax(callback,method,url) { method = method || ''POST''; callback = callback || success;//a default CB, just logs/alerts the response url = url || getUrl();//makes default url /currentController/ajax var xhr = createXHRObject();//try{}catch etc... xhr.open(method,url,true); xhr.setRequestMethod(''X-Requested-with'',''XMLHttpRequest''); xhr.setRequestHeader(''Content-type'',''application/x-www-form-urlencoded''); xhr.setRequestHeader(''Accept'',''*/*''); xhr.onreadystatechange = function() { callback.apply(xhr); } return function(data) { //do some checks on data before sending: data.hasOwnProperty(''user'') etc... xhr.send(data); } }

Todo bastante sencillo, excepto por la onreadystatechange llamada onreadystatechange . Noté algunos problemas con IE al vincular el controlador directamente: xhr.onreadystatechange = callback; , de ahí la función anónima. No sé por qué, pero creo que esta es la forma más fácil de hacerlo funcionar.

Como dije, estoy usando mucha delegación de eventos, por lo que puede imaginarse que puede ser útil tener acceso al elemento / evento real que activó la llamada ajax. Así que tengo algunos controladores de eventos que se ven así:

function handleClick(e) { var target,parent,data,i; e = e || window.event; target = e.target || e.srcElement; if (target.tagName.toLowerCase() !== ''input'' && target.className !== ''delegateMe'') { return true; } parent = target; while(parent.tagName.toLowerCase() !== ''tr'') { parent = parent.parentNode; } data = {}; for(i=0;i<parent.cells;i++) { data[parent.cells[i].className] = parent.cells[i].innerHTML; } //data looks something like {name:''Bar'',firstName:''Foo'',title:''Mr.''} i = prepareAjax((function(t) { return function() { if (this.readyState === 4 && this.status === 200) { //check responseText and, if ok: t.setAttribute(''disabled'',''disabled''); } } })(target)); i(data); }

Como puede ver, la onreadystatechange llamada onreadystatechange es el valor de retorno de una función, que proporciona la referencia al elemento de target cuando se llama a la devolución de llamada. Gracias a la delegación de eventos, ya no tengo que preocuparme por los eventos que puedan estar ligados a ese elemento, cuando decido eliminarlo del DOM (lo cual hago a veces).
En mi opinión, sin embargo, el objeto de llamada de la función de devolución de llamada puede ser demasiado para el motor JScript de IE y su recolector de basura:

Evento ==> controlador ==> prepareAjax es una secuencia de llamada bastante normal, pero el argumento de devolución de llamada:

[luego. func (argumento t = objetivo) devuelve anon. F (tiene acceso a t, que a su vez se remite al objetivo)]
===> pasó a una función de devolución de llamada anónima, llamada usando el método .apply para el objeto xhr, a su vez una variable privada para la función prepareAjax

He probado esta "construcción" en FF y Chrome. Funciona muy bien allí, pero ¿sería este tipo de callstack de cierre al cierre al cierre, en cada ocasión pasando una referencia a un elemento DOM ser un problema en IE (especialmente versiones anteriores a IE9)?

No, no voy a usar jQuery u otras librerías. Me gusta JS puro, y quiero saber todo lo que pueda sobre este lenguaje seriamente subestimado. Los fragmentos de código no son ejemplos reales de copiar y pegar, pero proporcionan, IMO, una buena representación de cómo estoy usando delegación, cierres y devoluciones de llamadas a lo largo de mi secuencia de comandos. Entonces, si alguna sintaxis no es correcta, no dude en corregirla, pero de eso no se trata esta pregunta, por supuesto.


Solía ​​trabajar con el ex administrador de programas para JavaScript en Microsoft, en un proyecto EcmaScript (err .. JScr ... JavaScript) que no es de navegador. Tuvimos algunas largas discusiones sobre cierres. Al final, el punto es que son más difíciles de GC, no imposibles. Tendría que leer la discusión de DC sobre cómo la MS está "equivocada" porque los cierres causan fugas de memoria, ya que en las implementaciones más antiguas de IE, los cierres eran ciertamente problemáticos porque eran muy difíciles de recolectar con la implementación de MS . Me parece extraño que un tipo de Yahoo intentara decirle a los arquitectos de MS que un problema conocido con su código estaba en otra parte. Por mucho que aprecio su trabajo, no veo qué base tenía allí.

Recuerde, el artículo al que hace referencia anteriormente se refiere a IE6, ya que IE7 aún estaba en desarrollo en el momento de su redacción.

Como un aparte - gracias a Dios, IE6 está muerto (no hagas que desentierre las imágenes del funeral). Aunque, no olvides su legado ... Todavía no he visto a nadie argumentar con credibilidad que no era el mejor navegador del mundo en el primer día de su lanzamiento. El problema fue que ganaron la guerra de los navegadores. . Y entonces, en lo que equivale a uno de los errores más grandes de su historia, despidieron al equipo y permaneció estancado durante casi 5 años. El equipo de IE se redujo a 4 o 5 muchachos que corrigieron errores durante años, creando una gran fuga de cerebros y quedando atrás de la curva dramáticamente. En el momento en que volvieron a contratar al equipo y se dieron cuenta de dónde estaban, estaban años atrás debido a la gran dificultad de lidiar con un código monolítico que nadie entendía realmente. Esa es mi perspectiva como interna en la empresa, pero no directamente vinculada a ese equipo.

Recuerda también, IE nunca optimizado para cierres porque no había ProtoypeJS (diablos, no había Rails), y jQuery apenas era un rayo en la cabeza de Resig.

En el momento de la redacción, también seguían apuntando a máquinas con 256 megas de RAM, que tampoco habían terminado al final de su vida útil.

Pensé que era justo entregar esta lección de historia, después de hacerme leer todo su libro de una pregunta.

Al final, mi punto es que estás haciendo referencia a material que está muy anticuado. Sí, evite los cierres en IE6, ya que causan pérdidas de memoria, pero ¿qué no en IE6?

Al final, es un problema que MS ha abordado y continúa abordando. Vas a hacer un cierto nivel de cierres, ese fue el caso, incluso en ese momento.

Sé que hicieron un trabajo pesado en esta área alrededor de IE8 (ya que mi proyecto innombrable usó el motor de JavaScript estándar no actual), y ese trabajo ha continuado en IE9 / 10. StatCounter (http://gs.statcounter.com/) sugiere que IE7 ha reducido a una cuota de mercado del 1,5%, frente al 6% de hace un año, y en el desarrollo de sitios "nuevos", IE7 se vuelve cada vez menos relevante. También puede desarrollar NetScape 2.0, que introdujo el soporte de JavaScript, pero eso sería un poco menos tonto.

Realmente ... No intentes optimizar demasiado por el bien de un motor que ya no existe.