[jquery] Jquery memory leak patterns

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Klopt het dat dit een memory leak oplevert?

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
myobject = function() {
  this.name = 'my object';
}

myobject.prototype.myFunction = function() {
 var myObject = this;
 jQuery('#myDiv').click(function(){
   var name = myObject.name;
   this.html(name);
 });
}


In 'myFunction' maak ik namelijk een verwijzing naar 'myObject', waaraan ik vervolgens refereer in de click-closurefunctie.

Zo ja, klopt het dan ook dat het volgende wel goed gaat?

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
myobject = function() {
  this.name = 'my object';
}

myobject.prototype.myFunction = function() {
 jQuery('#myDiv').data('myObject', this);

 jQuery('#myDiv').click(function(){
   var myObject = jQuery(this).data('myObject');

   jQuery(this).html(myObject.name);
 });
}


Ik meen ergens gelezen te hebben dat het zo goed gaat, omdat jQuery alle referenties die met .data gemaakt zijn, kan wissen. Maar ik kan het betreffende artikel niet meer terugvinden...

Memory leaks geven me 8)7

[ Voor 2% gewijzigd door Rekcor op 08-11-2011 15:09 . Reden: foutje + eerste voorbeeld niet goed ]


Acties:
  • 0 Henk 'm!

  • Grijze Vos
  • Registratie: December 2002
  • Laatst online: 28-02 22:17
Ik ben geen JS guru, maar is het niet handiger om (als je met jQ werkt) jQuery widgets te schrijven ipv prototypes schrijven?

Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Misschien, maar het ging me om het voorbeeld: het refereren naar een object in een closure, en vervolgens dat doen via Jq data.

Acties:
  • 0 Henk 'm!

  • Miyamoto
  • Registratie: Februari 2009
  • Laatst online: 14-09 21:46
Kun je dat niet makkelijk testen door een dergelijke sequence vaak genoeg uit te voeren en naar het geheugengebruik van de browser te kijken?

De onderliggende vraag is dat je een memory leak hebt, maar niet weet waar deze vandaan komt?

Acties:
  • 0 Henk 'm!

  • NetForce1
  • Registratie: November 2001
  • Laatst online: 15:57

NetForce1

(inspiratie == 0) -> true

Rekcor schreef op maandag 07 november 2011 @ 15:01:
Klopt het dat dit een memory leak oplevert?

JavaScript:
1
2
3
4
5
6
7
8
9
10
myobject = function() {
  this.name = 'my object';
}

myobject.prototype.myFunction = function() {
 var myObject = this;
 jQuery('#myDiv').click(function(){
   this.html(myObject.name);
 });
}


In 'myFunction' maak ik namelijk een verwijzing naar 'myObject', waaraan ik vervolgens refereer in de click-closure.

Zo ja, klopt het dan ook dat het volgende wel goed gaat?

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
myobject = function() {
  this.name = 'my object';
}

myobject.prototype.myFunction = function() {
 jQuery('#myDiv').data('myObject', this);

 jQuery('#myDiv').click(function(){
   var myObject = jQuery(this).data('myObject');

   jQuery(this).html(myObject.name);
 });
}


Ik meen ergens gelezen te hebben dat het zo goed gaat, omdat jQuery alle referenties die met .data gemaakt zijn, kan wissen. Maar ik kan het betreffende artikel niet meer terugvinden...

Memory leaks geven me 8)7
Event listeners die je met jQuery registreerd worden ook in data opgeslagen, dus die worden ook gewist. Je eerste voorbeeld lijkt me dan ook niet te leaken op het eerste gezicht. Overigens moet je dan wel jQuery gebruiken om het element uit je DOM te halen, anders kan jQuery ook niet weten dat er iets uit de data-store moet.

De wereld ligt aan je voeten. Je moet alleen diep genoeg willen bukken...
"Wie geen fouten maakt maakt meestal niets!"


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Miyamoto schreef op maandag 07 november 2011 @ 18:55:
Kun je dat niet makkelijk testen door een dergelijke sequence vaak genoeg uit te voeren en naar het geheugengebruik van de browser te kijken?
Ik geloof niet zo in testen, zeker niet als het complexe (web)applicaties betreft. Het is beter om naar je code te kijken, om lekken te voorkomen.

Ik weet dus niet of ik een lek heb, maar wil graag voorkomen dat er lekken komen :)

Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 17:26

Bosmonster

*zucht*

jQuery wist niet uit zichzelf user-set data. Het hele idee daarvan is juist dat het behouden blijft en je de optie geeft data te koppelen aan de dom.

Maar data gaan misbruiken als een soort closure is wel redelijk ranzig.

Acties:
  • 0 Henk 'm!

  • NetForce1
  • Registratie: November 2001
  • Laatst online: 15:57

NetForce1

(inspiratie == 0) -> true

Bosmonster schreef op dinsdag 08 november 2011 @ 08:28:
jQuery wist niet uit zichzelf user-set data. Het hele idee daarvan is juist dat het behouden blijft en je de optie geeft data te koppelen aan de dom.
Niet helemaal uit zichzelf, maar wel als je bijv. .empty() aanroept op een element, dan worden van de descendents daarvan de data gewist.

De wereld ligt aan je voeten. Je moet alleen diep genoeg willen bukken...
"Wie geen fouten maakt maakt meestal niets!"


Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 17:26

Bosmonster

*zucht*

Maar dan bestaan die nodes ook niet meer ;)

In het voorbeeld van TS wordt de data (gelukkig) niet automatisch zomaar verwijderd.

[ Voor 50% gewijzigd door Bosmonster op 08-11-2011 10:27 ]


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Bosmonster schreef op dinsdag 08 november 2011 @ 10:25:
Maar dan bestaan die nodes ook niet meer ;)

In het voorbeeld van TS wordt de data (gelukkig) niet automatisch zomaar verwijderd.
Maar, leidt het voorbeeld van TS (en dan met name het bovenste voorbeeld) ook tot memory leaks?

En waarom is de .data-optie ranzig?

Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 17:26

Bosmonster

*zucht*

Waarom denk je dat het tot memory leaks zou leiden? Wat is je onderbouwing?

De tweede optie is niet per definitie ranzig (eigenlijk is het best prima op de manier waarop je het nu doet). Wat wel ranzig is is om data gebruiken als vervanging voor closures (en dat zie ik helaas nog wel eens voorkomen).

Je doet in feite wel hetzelfde, maar met een betere bedoeling, namelijk je interne object koppelen aan je dom-node. En dat is een prima doel.

Waar je dan wel op moet letten is dat je die koppeling ook weer ongedaan maakt als je je interne object verwijdert. Een destructor dus die je data ook weer cleart.

[ Voor 34% gewijzigd door Bosmonster op 08-11-2011 10:35 ]


Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 15:54

crisp

Devver

Pixelated

Wat versta je precies onder een 'memory leak'? Het lijkt me dat de garbage collection de referentie prima kan opruimen zodra het element zelf wordt verwijderd of de eventlistener wordt verwijderd. Referenties naar JS objecten opslaan in de DOM leidt in IE (<=7) iig wel tot memoryleaks.

Intentionally left blank


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
myobject = function() {
  this.name = 'my object';
}

myobject.prototype.myFunction = function() {
 var myObject = this;
 jQuery('#myDiv').click(function(){
   var name = myObject.name;
   this.html(name);
 });
}


Maar als ik '#myDiv' verwijder uit de DOM (dmv jQuery('#myDiv').remove()), dan kan de garbage collector hem toch niet opruimen, aangezien myObject dan nog bestaat?

In het tweede voorbeeld kan garbage collector wel de '#myDiv' opruimen, want je mag aannemen dat jQuery alle variabelen in ".data" op null zet...

[ Voor 3% gewijzigd door Rekcor op 08-11-2011 15:09 . Reden: voorbeeld niet goed ]


Acties:
  • 0 Henk 'm!

  • NetForce1
  • Registratie: November 2001
  • Laatst online: 15:57

NetForce1

(inspiratie == 0) -> true

Rekcor schreef op dinsdag 08 november 2011 @ 15:08:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
myobject = function() {
  this.name = 'my object';
}

myobject.prototype.myFunction = function() {
 var myObject = this;
 jQuery('#myDiv').click(function(){
   var name = myObject.name;
   this.html(name);
 });
}


Maar als ik '#myDiv' verwijder uit de DOM (dmv jQuery('#myDiv').remove()), dan kan de garbage collector hem toch niet opruimen, aangezien myObject dan nog bestaat?

In het tweede voorbeeld kan garbage collector wel de '#myDiv' opruimen, want je mag aannemen dat jQuery alle variabelen in ".data" op null zet...
myDiv kan wel opgeruimt worden volgens mij, want myObject heeft geen referentie naar myDiv.

De wereld ligt aan je voeten. Je moet alleen diep genoeg willen bukken...
"Wie geen fouten maakt maakt meestal niets!"


Acties:
  • 0 Henk 'm!

  • JohnStaakke
  • Registratie: Oktober 2007
  • Laatst online: 09-09 15:17
NetForce1 schreef op dinsdag 08 november 2011 @ 15:16:
[...]

myDiv kan wel opgeruimt worden volgens mij, want myObject heeft geen referentie naar myDiv.
Volgens mij ook.

Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
crisp schreef op dinsdag 08 november 2011 @ 10:36:
Wat versta je precies onder een 'memory leak'? Het lijkt me dat de garbage collection de referentie prima kan opruimen zodra het element zelf wordt verwijderd of de eventlistener wordt verwijderd. Referenties naar JS objecten opslaan in de DOM leidt in IE (<=7) iig wel tot memoryleaks.
Dit is inderdaad de kern v/h verhaal. Dat is waarom jQuery aan DOM nodes een GUID toekent en deze vervolgens als index gebruikt om data behorende bij nodes in een van de DOM vrijstaande store te manipuleren.

Omdat ook event handlers op deze manier bewaard worden kan er nooit een circulaire referentie ontstaan. Dit wil zeggen dat hoe dan ook de garbage collector zijn werk kan doen om geheugen behorende bij DOM nodes op te ruimen. Er kan dan hoogstens wat rommel in de vrijstaande data store achterblijven, maar die wordt als ik me goed herinner gepruned.
Rekcor schreef op dinsdag 08 november 2011 @ 10:28:
[...]

Maar, leidt het voorbeeld van TS (en dan met name het bovenste voorbeeld) ook tot memory leaks?

En waarom is de .data-optie ranzig?
Nee, het bovenste voorbeeld leidt om bovenstaande redenen niet tot een (permanente) memory leak.

Verder is de .data-optie ranzig omdat er veel betere opties zijn om dit scenario af te handelen. $.fn.data is een constructie waarmee je snel ongestructureerde rommel kunt krijgen als je het te pas en te onpas gebruikt.

Als je echt data aan een handler mee wilt geven, dan doe je dat m.b.v. event.data. Verder is het trouwens beter voor de leesbaarheid van je code om niet allemaal handlers 'inline' te schrijven, maar ze als methods op je prototype te zetten en via jQuery.proxy de context juist te zetten. EN je kunt je prototype zelf ook een stuk leesbaarder noteren :

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
window.MyObject = function() {
  var me = this;

  me.name = 'my object';
}

window.MyObject.prototype = {
 /**
  * Initializes a new instance of MyObject
  */
 constructor : window.MyObject,

 /**
  * Does some work ...
  */
 myFunction : function() {
   var me = this, elem;
 
    elem = jQuery( "#myDiv" );
    elem.bind( "click", el, jQuery.proxy( me._myClickHandler, me ));
  }

  /**
   * @private
   * Handles click events on the associated DOM element.
   * @param {jQuery.Event} An event object, normalized by jQuery.
   */
  _myClickHandler : function( event ) {
    var me = this, elem = event.data;
  
    elem.html( me.name );
  }
};


(Strikt genomen is het doorgeven v/h element in dit soort situaties trouwens niet eens nodig. Je zou namelijk event.currentTarget moeten kunnen gebruiken.)
Grijze Vos schreef op maandag 07 november 2011 @ 15:51:
Ik ben geen JS guru, maar is het niet handiger om (als je met jQ werkt) jQuery widgets te schrijven ipv prototypes schrijven?
Eigenlijk is $.widget een class factory die ook prototypes aanmaakt, maar het schrijft (en nog belangrijker: leest!) inderdaad vele malen makkelijker. Ik zou alleen willen dat er een degelijke manier was om namespaces te gebruiken zodat $.fn niet vervuilt raakt met allerlei extra functies en dat de factory niet zo sterk verwoven was met $.Widget, maar ook gebruikt kon worden voor classes in het algemeen. (Daarvoor gebruik ik nu een eigen geschreven class factory.)

[ Voor 0% gewijzigd door R4gnax op 14-11-2011 21:45 . Reden: Bug in voorbeeld code ]


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Bedankt voor jullie reacties! Ik leer een hoop bij!

Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 05-09 14:39

_Thanatos_

Ja, en kaal

Ik mag toch aannemen dat dit afhankelijk is van een bepaalde javascript engine.

Je mag toch verwachten dat hedendaagse javascript engines wel in staat zijn om geheugenlekken niet meer te veroorzaken. In javascript ben je nml helemaal niet bezig met het alloceren en vrijgeven van blokken geheugen, zoals je deed in een 3G-programmeertaal. Javascript is een hogere taal die draait in een sandbox/VM waarvan de host verantwoordelijk is voor het alloceren en vrijgeven van geheugen. AL het geheugen dat je in javascript "alloceert" wordt beheert door de engine, en het is dan dus aan de engine om te weten wanneer objecten niet langer gerefereerd worden en vrijgegeven kunnen worden.

Je kunt natuurlijk wel constructies verzinnen die ervoor zorgen dat een object tot de eerstvolgende reload gebruikt wordt, maar dat is geen geheugenlek. Dat is geheugengebruik.

Gewoon nette code schrijven dus. JSLint laten valideren ook vooral. En zorgen dat er geen errors in je code zitten, ook geen errors die javascript toch stiekem tolereert.

deze reactie is natuurlijk te verwachten in een topic als dit heh ;)

日本!🎌

Pagina: 1