SorgenkindMech: MemLeak IE Ajax

Huhu ihr Profis!

Ich stehe mal wieder vor einem kleinen Rätsel ;)

Ich habe ja gelesen, dass bei, ich nenne es mal gegenseitigen Referenzierungen z. B. der IE nicht erkennen kann, ob er den Speicher wieder freigeben kann.

nun habe ich folgende Funktion, zu erkennen ist eine leicht abgewandelte version einer standard AJAX-funktion aus dem "Lehrbuch" *g*

var running_ajax_requests = new Array();  
function request_fkt(ziel,post,parameter)  
{  
	if(!post) ziel="../../ajax.php?" + ziel;  
	else ziel="../../ajax.php";  
	xmlHttp = null;  
	try {  
		// Mozilla, Opera, Safari sowie Internet Explorer (ab v7)  
		xmlHttp = new XMLHttpRequest();  
	} catch(e) {  
		try {  
			// MS Internet Explorer (ab v6)  
			xmlHttp  = new ActiveXObject("Microsoft.XMLHTTP");  
		} catch(e) {  
			try {  
				// MS Internet Explorer (ab v5)  
				xmlHttp  = new ActiveXObject("Msxml2.XMLHTTP");  
			} catch(e) {  
				xmlHttp  = null;  
			}  
		}  
	}  
	if (xmlHttp)  
	{  
		running_ajax_requests.push(xmlHttp);  
		xmlHttp=null;  
		akt_ajax_req_index=running_ajax_requests.length-1;  
		if(!post) running_ajax_requests[akt_ajax_req_index].open('GET', ziel, true);  
		else running_ajax_requests[akt_ajax_req_index].open('POST', ziel, true);  
		running_ajax_requests[akt_ajax_req_index].onreadystatechange = function ()  
		{  
			if (this.readyState == 4) {  
				if(this.responseText!="1")  
				{  
					response=this.responseText;  
					try  
					{  
						// alert(response);  
						eval(response);  
					}  
					catch(e)  
					{  
						// fehlerbehandlung  
					}  
				}  
			}  
			for(i=(running_ajax_requests.length-1);i>=0;i--)  
			{  
				if(running_ajax_requests[i].readyState==4)  
				{  
					running_ajax_requests[i]=null;  
					running_ajax_requests.splice(i,1);  
				}  
			}  
		};  
		if(!post) running_ajax_requests[akt_ajax_req_index].send(null);  
		else  
		{  
			running_ajax_requests[akt_ajax_req_index].setRequestHeader("Content-type", "application/x-www-form-urlencoded");  
			running_ajax_requests[akt_ajax_req_index].setRequestHeader("Content-length", parameter.length);  
			running_ajax_requests[akt_ajax_req_index].setRequestHeader("Connection", "close");			  
			running_ajax_requests[akt_ajax_req_index].send(parameter);  
		}  
	}  
	else return "konnte XMLHttpRequest nicht initialisieren";  
}  

ich habe also versucht, indem ich ein array mit allen laufenden requests habe und dieses jedesmal nach abgeschlossenen requests durchsuche um diese dann zu entfernen, diesen speicherfreigabefehler zu umgehen, jedoch sehe ich im taskmanager, wie der speicherbedarf des IE von 60 MB immer stetig steigt .. nach 10 minuten nun steht er bei 577 MB ...

Firefox geht mit 106 MB ins Rennen, und scheint dann immer so zwischen 150 und 230MB zu schwanken, ergo scheint hier der Müllsammler soweit zu funktionieren

hat jemand eine idee, wie ich das sauber lösen kann? (nein, ich möchte keine fertigen frameworks dazu verwenden ;)

LG euer SorgenkindMech

  1. Hallo SorgenkindMech,

      			running\_ajax\_requests[i]=null;  
    

    hier überschreibst du ein Arrayelement mit null. Ich würde mal versuchen, das Objekt mit delete zu entfernen.

    Gruß, Jürgen

    1. Hallo SorgenkindMech,

        			running\_ajax\_requests[i]=null;  
      

      hier überschreibst du ein Arrayelement mit null. Ich würde mal versuchen, das Objekt mit delete zu entfernen.

      Gruß, Jürgen

      Hallo Jürgen,

      habs jetzt mal auf

        
      running_ajax_requests[i]=null;  
      delete running_ajax_requests[i];  
      
      

      geändert, test läuft ...

      1. Hallo SorgenkindMech,

        habs jetzt mal auf

        running_ajax_requests[i]=null;
        delete running_ajax_requests[i];

        
        > geändert, test läuft ...  
          
        das dürfte nichts bringen, da du ja das delete auf null anwendest.  
          
        Gruß, Jürgen  
        
        
        1. Hallo SorgenkindMech,

          habs jetzt mal auf

          running_ajax_requests[i]=null;
          delete running_ajax_requests[i];

          
          > > geändert, test läuft ...  
          >   
          > das dürfte nichts bringen, da du ja das delete auf null anwendest.  
          >   
          > Gruß, Jürgen  
          >   
            
          Hallo Jürgen,  
            
          damit hattest du recht  
          die Änderung auf:  
          ~~~javascript
          			for(i=(running_ajax_requests.length-1);i>=0;i--)  
          			{  
          				if(running_ajax_requests[i]==null || running_ajax_requests[i].readyState==4)  
          				{  
          					delete running_ajax_requests[i];  
          				}  
          			}  
          
          

          scheint tatsächlich abhilfe zu schaffen, er dümpelt so bei .. moment ... hm bis eben war er so bei 300 MB, jetz bei 430 ... ich beobachte das mal weiter ...

          dennoch erstmal danke bis hierhin

          1. Hallo SorgenkindMech,

              		if(running\_ajax\_requests[i]==null  
            

            warum diese Abfrage?

            Gruß, Jürgen

            1. Hallo SorgenkindMech,

                		if(running\_ajax\_requests[i]==null  
              

              warum diese Abfrage?

              Gruß, Jürgen

              öhm überbleibsel aus der vorherigen funktionsweise, wo ich ja das element null gesetzt hatte ^^

              also im moment steht er bei knapp 1,1 GB ... es scheint also nicht wirklich geholfen haben ;(

              hast du noch eine andere idee?

              zur vollständigkeit die aktuelle funktion nochmal:

              var running_ajax_requests = new Array();  
              function request_fkt(ziel,post,parameter)  
              {  
              	if(!post) ziel="../../ajax.php?" + ziel;  
              	else ziel="../../ajax.php";  
              	xmlHttp = null;  
              	try {  
              		// Mozilla, Opera, Safari sowie Internet Explorer (ab v7)  
              		xmlHttp = new XMLHttpRequest();  
              	} catch(e) {  
              		try {  
              			// MS Internet Explorer (ab v6)  
              			xmlHttp  = new ActiveXObject("Microsoft.XMLHTTP");  
              		} catch(e) {  
              			try {  
              				// MS Internet Explorer (ab v5)  
              				xmlHttp  = new ActiveXObject("Msxml2.XMLHTTP");  
              			} catch(e) {  
              				xmlHttp  = null;  
              			}  
              		}  
              	}  
              	if (xmlHttp)  
              	{  
              		running_ajax_requests.push(xmlHttp);  
              		xmlHttp=null;  
              		akt_ajax_req_index=running_ajax_requests.length-1;  
              		if(!post) running_ajax_requests[akt_ajax_req_index].open('GET', ziel, true);  
              		else running_ajax_requests[akt_ajax_req_index].open('POST', ziel, true);  
              		running_ajax_requests[akt_ajax_req_index].onreadystatechange = function ()  
              		{  
              			if (this.readyState == 4) {  
              				if(this.responseText!="1")  
              				{  
              					response=this.responseText;  
              					try  
              					{  
              						// alert(response);  
              						eval(response);  
              					}  
              					catch(e)  
              					{  
              						// fehlerbehandlung  
              					}  
              				}  
              			}  
              			for(i=(running_ajax_requests.length-1);i>=0;i--)  
              			{  
              				if(running_ajax_requests[i]==null || running_ajax_requests[i].readyState==4)  
              				{  
              					delete running_ajax_requests[i];  
              				}  
              			}  
              		};  
              		if(!post) running_ajax_requests[akt_ajax_req_index].send(null);  
              		else  
              		{  
              			running_ajax_requests[akt_ajax_req_index].setRequestHeader("Content-type", "application/x-www-form-urlencoded");  
              			running_ajax_requests[akt_ajax_req_index].setRequestHeader("Content-length", parameter.length);  
              			running_ajax_requests[akt_ajax_req_index].setRequestHeader("Connection", "close");			  
              			running_ajax_requests[akt_ajax_req_index].send(parameter);  
              		}  
              	}  
              	else return "konnte XMLHttpRequest nicht initialisieren";  
              }  
              
              

              DANKE!!

  2. Das delete wird nichts bringen, da es ja nur Properties entfern. Null setzen ist schon der richtige Weg um Referenzen aufzulösen.
    Nur sehe ich bei dir nicht, wie du durch deine zusätzliche Referenz gegenseitige Referenzen auflösen willst! Welche denn überhaupt?
    Die Schleife in jeder onreadystatechange-Funktion ist auch überflüssig, du reagierst ja für jeden Request schon auf das Event und kannst dann auch gleich am Ende deine Referenz nullen.
    Geht denn der Speicher nach einer Stunde immer noch hoch oder pegelt sich das bei einem Wert ein?

    1. Das delete wird nichts bringen, da es ja nur Properties entfern. Null setzen ist schon der richtige Weg um Referenzen aufzulösen.
      Nur sehe ich bei dir nicht, wie du durch deine zusätzliche Referenz gegenseitige Referenzen auflösen willst! Welche denn überhaupt?
      Die Schleife in jeder onreadystatechange-Funktion ist auch überflüssig, du reagierst ja für jeden Request schon auf das Event und kannst dann auch gleich am Ende deine Referenz nullen.
      Geht denn der Speicher nach einer Stunde immer noch hoch oder pegelt sich das bei einem Wert ein?

      HUHU!

      naja ursprünglich hatte ich gedacht, dass ich aus der funktion heraus das objekt, auf dass sich die funktion ja bezieht nicht löschen könnte, da sich ja damit die funktion selbst auflösen würde obwohl sie gerade läuft.

      also dachte ich mir ok, dann packst das ganze in ein globales array und prüfe jedes mal ob eine der offenen requests noch existiert, obwohl es fertig ist. tests mittels alert haben ergeben, dass das array auch meist eine länge von 0 hat. dennoch geht der speicherverbrauch in die höhe. einpegeln tut er sich dann, wenn der ram komplett aufgebraucht ist, dann geht der rechner in die knie ^^
      eben war er bei 1,5 GB RAM .. das ist natürlich unzumutbar.

      1. naja ursprünglich hatte ich gedacht, dass ich aus der funktion heraus das objekt, auf dass sich die funktion ja bezieht nicht löschen könnte, da sich ja damit die funktion selbst auflösen würde obwohl sie gerade läuft.

        Nein, du setzt ja nur die Referenz auf das Requestobjekt in der Funktion request_fkt null. Den Eventhandler löschst du damit nicht solange er noch läuft, dafür sorgt schon der IE, selbst wenn du die onreadystatechange-Property nullst. Der hat seine eigene Referenz auf die gerade ausgeführte Funktion.

        tests mittels alert haben ergeben, dass das array auch meist eine länge von 0 hat.

        Du entfernst es es ja auch immer nach Abschluss! Also immer den gerade laufenden Request.

        dennoch geht der speicherverbrauch in die höhe. einpegeln tut er sich dann, wenn der ram komplett aufgebraucht ist, dann geht der rechner in die knie ^^
        eben war er bei 1,5 GB RAM .. das ist natürlich unzumutbar.

        Was machst du denn mit den Daten? Wenn du die jedesmal irgendwo einbaust, und alte nicht wieder austrägst verbrauchst du immer mehr Speicher.

        1. naja ursprünglich hatte ich gedacht, dass ich aus der funktion heraus das objekt, auf dass sich die funktion ja bezieht nicht löschen könnte, da sich ja damit die funktion selbst auflösen würde obwohl sie gerade läuft.
          Nein, du setzt ja nur die Referenz auf das Requestobjekt in der Funktion request_fkt null. Den Eventhandler löschst du damit nicht solange er noch läuft, dafür sorgt schon der IE, selbst wenn du die onreadystatechange-Property nullst. Der hat seine eigene Referenz auf die gerade ausgeführte Funktion.

          genau deswegen dachte ich mir, packst es in ein globales array

          tests mittels alert haben ergeben, dass das array auch meist eine länge von 0 hat.
          Du entfernst es es ja auch immer nach Abschluss! Also immer den gerade laufenden Request.

          öhm ja .. nach abschluss will ich es ja auch entfernen, nicht mittendrin ;)

          dennoch geht der speicherverbrauch in die höhe. einpegeln tut er sich dann, wenn der ram komplett aufgebraucht ist, dann geht der rechner in die knie ^^
          eben war er bei 1,5 GB RAM .. das ist natürlich unzumutbar.
          Was machst du denn mit den Daten? Wenn du die jedesmal irgendwo einbaust, und alte nicht wieder austrägst verbrauchst du immer mehr Speicher.

          letztendlich ist es eine riesen tabelle. hier werden zellen geleert (.innerHTML="") und dann mit neuen daten befüllt, sprich die tabelle wird "aktuell" gehalten

          es geht halt schneller nur die ändeurngen nachzuladen, als jedesmal die gesamte tabelle laden zu lassen

          hast du denn eine idee?

          aktuelle version der funktion:

          var running_ajax_requests = new Array();  
          function request_fkt(ziel,post,parameter)  
          {  
          	if(!post) ziel="../../ajax.php?" + ziel;  
          	else ziel="../../ajax.php";  
          	xmlHttp = null;  
          	try {  
          		// Mozilla, Opera, Safari sowie Internet Explorer (ab v7)  
          		xmlHttp = new XMLHttpRequest();  
          	} catch(e) {  
          		try {  
          			// MS Internet Explorer (ab v6)  
          			xmlHttp  = new ActiveXObject("Microsoft.XMLHTTP");  
          		} catch(e) {  
          			try {  
          				// MS Internet Explorer (ab v5)  
          				xmlHttp  = new ActiveXObject("Msxml2.XMLHTTP");  
          			} catch(e) {  
          				xmlHttp  = null;  
          			}  
          		}  
          	}  
          	if (xmlHttp)  
          	{  
          		running_ajax_requests.push(xmlHttp);  
          		xmlHttp=null;  
          		akt_ajax_req_index=running_ajax_requests.length-1;  
          		if(!post) running_ajax_requests[akt_ajax_req_index].open('GET', ziel, true);  
          		else running_ajax_requests[akt_ajax_req_index].open('POST', ziel, true);  
          		running_ajax_requests[akt_ajax_req_index].onreadystatechange = function ()  
          		{  
          			if (this.readyState == 4) {  
          				if(this.responseText!="1")  
          				{  
          					response=this.responseText;  
          					try  
          					{  
          						// alert(response);  
          						eval(response);  
          					}  
          					catch(e)  
          					{  
          						// fehlerbehandlung  
          					}  
          				}  
          			}  
          			for(i=(running_ajax_requests.length-1);i>=0;i--)  
          			{  
          				if(running_ajax_requests[i]==null || running_ajax_requests[i].readyState==4)  
          				{  
          					delete running_ajax_requests[i];  
          				}  
          			}  
          		};  
          		if(!post) running_ajax_requests[akt_ajax_req_index].send(null);  
          		else  
          		{  
          			running_ajax_requests[akt_ajax_req_index].setRequestHeader("Content-type", "application/x-www-form-urlencoded");  
          			running_ajax_requests[akt_ajax_req_index].setRequestHeader("Content-length", parameter.length);  
          			running_ajax_requests[akt_ajax_req_index].setRequestHeader("Connection", "close");			  
          			running_ajax_requests[akt_ajax_req_index].send(parameter);  
          		}  
          	}  
          	else return "konnte XMLHttpRequest nicht initialisieren";  
          }  
          
          

          ich danke auch dir für deine mithilfe!

  3. genau deswegen dachte ich mir, packst es in ein globales array

    Das verstehe ich nicht, weil ich ja versucht habe deutlich zu machen, dass das Sinnlos ist.

    öhm ja .. nach abschluss will ich es ja auch entfernen, nicht mittendrin ;)

    Dann könntest du auch das Array weglassen und am Ende der Funktion "xmlHttp = null;" schreiben.
    Wobei ich gerade sehe, dass xmlHttp bei dir eine globale Variable ist. Das ist aus mehreren gründen nicht gut. mach die lokal.

    hast du denn eine idee?

    Versuch erst mal rauszufinden, wo das leak ist. Helfen könnte dabei sIEve.

  4.   running\_ajax\_requests[akt\_ajax\_req\_index].onreadystatechange = function ()  
    

    Rein ins blaue geraten, wird hier nicht immer eine neue Funktion erstellt und dann einfach nur eine Referenz an onreadystatechange übergeben?
    Wenn ja, AFAIK kann man ja Funktionen nicht "löschen" wärend der Laufzeit, sprich, es werden immer mehr Funktionen erstellt, dessen Resourcen erst wieder freigegeben werden, wenn die Seite verlassen wird.
    Oder irre ich mich jetzt ganz gewaltig?

    MfG
    bubble

    1. Oder irre ich mich jetzt ganz gewaltig?

      Dafür gibt es die Garbage Collection, die nicht benötigte Objekte wieder weg schmeisst.
      Ob der IE jetzt hier feststellt, dass diese Funktion nicht mehr benötigt wird oder nicht, bleibt die Frage. Das könnte hier ein Problem sein, weil diese Funktion auf das Requestobjekt zugreift, das Requestobjekt aber auch diese Funktion in einer Property referenziert.

  5. Moin,

    eval(response);

    so gewollt?

    1. Moin,

      eval(response);

      so gewollt?

      ja durchaus so gewollt ;)