Speicherleck
-
Wenn ich dich richtig verstanden habe, dann zeigt sich das Problem durch die geworfene IOException. Richtig?
Wenn das so ist, wäre der Code, der die IOException auslöst hier von Nutzen.
Gruss Simon
-
Hallo Simon,
ja, es gibt gelegentlich diese Exception aber sie tritt nur undefiniert ein, die Funktion in der die Exception aufgetreten ist, wird bis zur Exception unngefähr 30 - 40 mal aufgerufen, oder sogar noch viel öffter.
Meint, ich könnte nicht sicher sagen wo das Problem genau herkommt, außerdem möchte ich den Code hier nicht vorzeigen. Das liegt vor allem daran, dass das ca. 1000 Zeilen sind.
Gelesen habe ich sehr viel im Netz, aber nichts hat mir so richtig geholfen.
Ich würde gerne den PerfMon verwenden (weil kostenfrei) um die Anwendung zu überprüfen.
Welche Werte muss ich mindestens für die Auswertung heranziehen?
-
Mit sos.dll kann man relativ gut Speicherlecks finden... noch besser sind graphische Tools die darauf aufbauen (z.B. .NET Memory Profiler):
http://memprofiler.com/Ich vermute, dass Du irgendwo ein Array hast wo Du nicht wieder freigeibst... oder Objekte am leben bleiben weil ein Delegate noch drauf lebt...
Dass der Speicher immer ansteigt ist eigentlich "normal". Dies ist bis zu einem gewissen Grad "normal". .NET nimmt sich so viel wie möglich/nötig. ABer wenn er immer ansteigt ist was faul... wenn er sich so bei 100 MB einpendelt ist es ok...
PS: Was soll den "Auflistungsanzahl in GC 0" sein (ich hasse die deutschen übersetzungen).
Interessanter wäre eher: "Speicher in GC0-2" und LOH
-
Ich habe mit Ants Profiler leider keine brauchbaren Ergebnisse erhalten. Aber ich werde Deinen Vorschlag die nächsten Tagen testen (memprofiler).
Mit dem PerfMon kann ich also nichts bewirken oder ermitteln?
Arrys lösche ich vorzeitig mit :
delete array
Kann man das auch für z.B. DateTime oder Srings machen?
Gibt
delete
den Spicher mit Sicherheit wieder frei?
-
In .NET löscht man nichts mit "delete"... sondern nur dadurch, dass man das Objekt nicht mehr verwendet und keine Verweise mehr darauf hat.
Ein "delete" auf managed objekte bewirkt entweder gar nichts oder es ruft "IDispose::Dispose()" auf.Mit dem Profiler bekommst Du nur ganz grob was raus. Mit einem FullDump und passenden Symbolen/EXE/DLLs (oft auch ohne Symbole) kannst Du hingegen einiges anfangen. Auch später noch, wenn Du die Dump-Datei in den MemProfiler lädst.
-
OK, danke erstmal für die Infos.
Ich mach mich dann mal an die MemProfiler.Happy new year!
-
Hallo Jochen,
ich habe zum MemProfiler fragen. Kennst Du Dich damit aus?
-
Frag halt!
-
Hallo Jochen,
hier nochmal zur Info, warum ich glabe, dass ich en Leck habe:
1. Andere Programme haben nach längerer Zeit (ca. 5 Wochen) Probleme und laufen nicht mehr richtig. Sie verursachen irgendwelche Fehler und stützen ab.
2. Man kann im Taskmanager über einen längeren Zeiraum hinweg beobachten, wie immer weniger virtueller Speicher zur Verfügung steht.Hauptsächlich laufen in meinem Formular 3 Timer.
Timer 1:
Hier für wird DateTime als Attribut im Formular angelegt, und im Konstruktor initialisiert.Im Event Timer - Tick wird dann im Sekundentakt die aktuelle Zeit ermittelt und in einem Label angezeigt. Im Timer wird also hauptsächlich das hier gemacht:
private: System::Void timer2_Tick(System::Object^ sender, System::EventArgs^ e) { label1->Text=dTime->Now.ToString(); }
Timer 2:
Hier wird lokal im Sekundentakt die Prozessliste ermittelt.private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) { listBox1->Items->Clear(); array<Process^>^runningProcesses=Process::GetProcesses(); for each(Process ^element in runningProcesses) listBox1->Items->Add(element->ProcessName); }
Timer 3:
Öffnet im Sekunentakt eine Datei und durchsucht diese auf spezielle Vorkommen.private: System::Void timer3_Tick(System::Object^ sender, System::EventArgs^ e) { StreamReader ^sReader=gcnew StreamReader("C:\\testdatei.log");//nur eine Zeile String^zeile; if(sReader->EndOfStream!=true) zeile=sReader->ReadLine(); //Mach was mit der Zeile sReader->Close(); }
Wenn ich das jetzt mit dem MemProfiler starte und auswerte sehe ich im RealTime View folgendes:
Alein durch den Timer für die Processliste wachsen alle drei Heaps, 0 -2 kontinuierlich.
Das gleiche passiert auch bei dem Timer für die Zeit und für die Datei, als dem SreamReader.Warum fallen die Werte nicht zurück, sondern wandern immer weiter durch die Heaps durch?
Zumindest bez. dem Process Timer meint MemProfiler, dass zu viele "undisposed" Instanzen vorliegen.
Aber was kann ich dagegen tun?
Oder ist das alles gar kein Problem??
-
Wie hast Du die Timer angelegt?
Zeige auch diesen Code.Simon
-
Die Timer sind keine besonderen Threads.
Sie sind ganz einfach aus der Toolleiste in das Formular integriert.
Mittels Steuerelement also.
-
Ich weiss... solltest sie auch disposen.
Genau wie die Process Instanzen aus Process::GetProcesses().Simon
-
Hast Du mal versucht, jeweils nur einen Timer laufen zu lassen um das Problem einzugrenzen?
Simon
-
1. Mit dem MemProfiler solltest Du *zwei* Speicherstände vergelichen... nur dadurch kannst DU was sinnvolles erhalten...
Lass also Dein Program mal 10 min. laufen und mache den ersten MemDump. Dann lass es mal 2h laufen und machen den zweiten MemDump. Dann werden automatisch die beiden Stände verglichen und Du siehst, was in der Zeit angestiegen ist.
2. Deine Beobachtungen bzgl. "Speicherleck" sind wertlos. Du kannst ein Speicherleck nur nachweisen, wenn Du das "Private Bytes" *Deines* Proesses beobachtest. Es spielt keine Rolle, was die anderen Prozesse machen!
3. Wenn Du eh nur die laufenden Prozesse aufliesten lassen willst, dann rate ich Dir dazu dies über die *native* Methoden zu machen; die sind wesentlich performanter und machen nicht so viel "blödsinn" wie die .NET Implementierung (welche via PerformanceCounter implemetiert ist).
http://msdn.microsoft.com/en-us/library/ms682629
-
@Fragesteller:
Ich würde ev. doch nochmal der IOException nachgehen. Immerhin bis Du weisst warum und wann sie auftritt.Simon
-
Genau genommen ist das hier nur ein Ausschnitt aus dem eigentlichen Programm, ein Test, bei dem ich auch die Timer einzeln laufen lassen habe.
Wie kann ich den StreamReader oder DateTime disposen?
Um die Prozessliste zu erhalten, lege ich doch eigentlich gar keine Instanz der Prozessklasse an?
Aber ich habe den Code hier mal geändert:private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) { listBox1->Items->Clear(); Process ^prozesse=gcnew Process(); for each(Process ^element in prozesse->GetProcesses()) listBox1->Items->Add(element->ProcessName); prozesse->Close(); }
Es hat sich aber nichts wirklich geändert. Die Heaps werden dennoch weiter belastet, obwohl das doch "short Life sein müsste, oder?
Was muss ich genau zum Disposen für Process aufrufen?
prozesse->Dispose(true);
geht nicht.
-
Sollte ich das mit der API auch für DateTime machen?
-
Noch ne Frage, wo finde ich im MemProfiler die Private Bytes Auflistung?
Ist das damit gemeint:
Registerkarte - Types - Live Bytes - Total? New ? oder Max?
Was ist mit den Werten von Gen0 - Gen2 in Real-Time? Ist es unbedeuten, dass diese dauernt erhöht werden?
-
Hier ein Bsp. um die Prozesse zu ermitteln und mittels Dispose() (durch delete aufgerufen) wieder freigegeben:
void f() { array<Process^>^ processes = Process::GetProcesses(); for each (Process^ process in processes) { listBox1->Items->Add(process->ProcessName); delete process; // ruft process->Dispose(); auf } }
Bei DateTime musst Du kein Dispose() aufrufen.
Jedoch kannst Du mal den Tip von Jochen befolgen und EnumProcesses(..) benutzen.
Simon
-
Um überhauot festustellen ob man ein Leck hat, ist im Folgenden Link
http://memprofiler.com/screenshots.aspx
in Screen "Native Memory" der "Private" relevant...