NancyG Geschrieben 14. Juni 2013 Geschrieben 14. Juni 2013 Hallo Leute, ich habe eine Anwendung die immer laufen soll! Nun ist es aber so, dass dort der Speicher *gefressen* wird. Also habe ich ich mal eine kleine Testanwendung geschrieben - und das Szenario vereinfacht nachgestellt. Es wird auf Kommando ein Thread gestartet - und auf Kommando auch wieder gestoppt (gelöscht). Leider wird bei diesem Vorgang der Speicher nicht wieder vollends freigegeben. Konsolenanwendung: Dim thTest As Threading.Thread Dim lineRead As String Sub Main() Do lineRead = Console.ReadLine() If Not commandParse(lineRead) Then Console.WriteLine("command not found: " & lineRead) End If If lineRead = "exit" Then Exit Do GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.WaitForPendingFinalizers() Loop End Sub Function commandParse(ByVal input As String) As Boolean Select Case input Case "exit" dis() Case "dmexit" dis() Case "dmcon" con() Case Else Return False End Select Return True End Function Sub myThread() While thTest.IsAlive Threading.Thread.Sleep(10) End While End Sub Sub con() If thTest Is Nothing Then thTest = New Threading.Thread(AddressOf myThread) thTest.Start() End If End Sub Sub dis() If Not thTest Is Nothing Then thTest.Abort() thTest = Nothing End If End Sub Ich habe mir das Tool .Net Memory Profiler installiert und mal geschaut, wo es denn klemmt. Was ich festgestellt habe, dass im .Net der Speicher wieder frei wird, nur der vMem und der heapMem vom System nicht mehr. Vorm Starten: Beim laufenden Thread: Nach beenden des Threads: Wo liegt das Problem - und wie kann ich es lösen? vG Nancy Zitieren
Guybrush Threepwood Geschrieben 14. Juni 2013 Geschrieben 14. Juni 2013 Windows gibt Speicher nicht "direkt frei" wenn du ein Programm beendest sondern dann wenn er benötigt wird. Das heißt nur weil z.B. im Taskmanager nach dem Beenden deines Programms noch mehr Arbeitsspeicher als in Verwendung angezeigt wird auls vorher heißt nicht das irgendwo ein Speicherleck ist. Das wäre erst der Fall wenn der Speiher wirklich benötigt werden würde aber nicht zur Verfügung stände. Genauso solltest du den GC nicht selber aufrufen, da gibt es eigentlich nur sehr selten Anlass zu. Der arbeitet nämlich auch so das er unnötige Arbeitslast vermeidet und seinen Speicher in bestimmten Abständen oder wenn wirklich Speicher benötigt wird aufräumt. Zitieren
lilith2k3 Geschrieben 14. Juni 2013 Geschrieben 14. Juni 2013 Rule #1 Don't. This is really the most important rule. It's fair to say that most usages of GC.Collect() are a bad idea and I went into that in some detail in the orginal posting so I won't repeat all that here. So let's move on to... When to call GC.Collect() - Rico Mariani's Performance Tidbits - Site Home - MSDN Blogs Du solltest bedenken, dass Du in der Regel mit C#/VB.NET sog. managed Code schreibst. Das heißt, dieser Code verhält sich anders als C/C++ - Code. Während Du unter C++ einen Destruktor hast, Du unter C/C++ Speicherblöcke nach belieben reservieren und wieder freigeben kannst, geht das bei managed Code nicht. Der Garbage Collector kümmert sich darum, wann welche Objekte wieder frei gegeben werden. Frei nach "Onkel Bob": »So you are writing Code in Java [man könnte auch .NET sagen]? Why care about memory anyways?« Zitieren
raiserle Geschrieben 15. Juni 2013 Geschrieben 15. Juni 2013 Falsch! Das Problem liegt an deinem: thTest.Abort() Lass mal deinen Thread normal beenden. Dann wird auch der Speicher frei. //globale variable zum beenden des Thread Dim thStopThread as Bool Sub dis() If Not thTest Is Nothing Then thStopThread = true End If End Sub Sub myThread() While thTest.IsAlive and not thStopThread Threading.Thread.Sleep(10) End While thTest = nothing thStopThread = false End Sub Ungetestet - sollte aber funktionieren. CreateThread: Remarks lesen @Guybrush Threepwood: Zeig mir mal bitte wo das steht! Windows gibt Speicher nicht frei! Sondern nur, wenn er benötigt wird. 1. Wird sehr wohl der Speicher freigegeben, wenn der letzte Thraed eines Prozesses beendet wird. 2. Wird der Speicher auch freigegeben, wenn man ihn über (malloc reserviert) free wieder freigibt. zu Net: Wie dort steht, war das ein Testfall, um den Speicherverbrauch zu messen/simulieren. Also spricht nichts gegen ein GC.Collect OT: Du solltest bedenken, dass Du in der Regel mit C#/VB.NET sog. managed Code schreibst. Das heißt, dieser Code verhält sich anders als C/C++ - Code. ... Ähm... hast du gelesen was sie geschrieben hat. Und hast du dir auch mal die Bilder angesehen? Zitieren
realgun Geschrieben 16. Juni 2013 Geschrieben 16. Juni 2013 Also Thread.Abort lässt den Thread auch "normal" beenden, er wird halt abgebrochen. Und zwar genau an der Stelle, wo er sich gerade befindet, dabei wird eine Excption im Thread ausgelöst. Und wenn man die "ThreadAbortException" nicht richtig behandelt, können dann keine Ressourcen mehr freigegeben werden, die man im Thread angelegt hat. Der Thread selber ist dann trotzdem beendet. Sauberer ist es aber auf jeden Fall mit einem Flag. (Gerade weil es ein so ein einfacher Testfall ist, hilft GC.Collect hier überhaupt nicht weiter. Im Codebeispiel spielt der aber sowieso keine Rolle, da der Screenshot nach dem Beenden des Threads gemacht wurde, also in der "DO-LOOP". Zum Speicherverbrauch messen eignen sich Profiler und oder Performance Counter. ) Aber jetzt auch mal was zur eigentlichen Frage @NancyG: Meinst Du wirklich die Werte aus den Zeilen "Kernel"? Das sind Systemwerte, auf die alle Anwendungen Einfluss haben (nicht nur Dein .NET-Code). Zitieren
NancyG Geschrieben 17. Juni 2013 Autor Geschrieben 17. Juni 2013 Hallo, danke für die Antworten. Ich habe jetzt weiter getestet. Aber es funktioniert auch nicht, wenn ich den Thread normal beenden lasse (über Flag). Die geöffneten Handles bleiben offen. Demzufolge wird auch der Speicher nicht freigegeben. Ich habe das ganze nochmal unter c++ getestet, dort wird der Thread beendet - und der Speicher ist wieder FREI. CloseHandle( CreateThread(....)); //handle gleich schliessen Aber wie kann man im .Net die Handles wieder schließen? Bei der Testanwendung sind nach dem Starten 104 Handles offen. Wird der Thread gestartet - sind es 109. Nach dem Beenden des Threads gehen die Handles nach Ewigkeiten wieder runter - auf 108! Was aber nicht die Ausgangssituation war. Es müssten doch wieder 104 werden. Wie oben schon geschrieben. Das ganze im C++ - macht genau - was man erwartet. Was muss ich an dem Code noch verändern, um das gewünschte Ergebnis zu erzielen - den Speicher wieder frei zu bekommen. vG Nancy Zitieren
Guybrush Threepwood Geschrieben 18. Juni 2013 Geschrieben 18. Juni 2013 @Guybrush Threepwood: Zeig mir mal bitte wo das steht! Windows gibt Speicher nicht frei! Sondern nur, wenn er benötigt wird. 1. Wird sehr wohl der Speicher freigegeben, wenn der letzte Thraed eines Prozesses beendet wird. 2. Wird der Speicher auch freigegeben, wenn man ihn über (malloc reserviert) free wieder freigibt. Deswegen die Anführungszeichen. Viele Leute gehen davon aus das wenn sie ein Programm beenden und dann zum Beispiel im Taskmanager sehen das der Speicherverbrauch trotzdem nicht wieder auf den Stand vor dem Programmstart zurückgegangen ist das ein Speicherleck in dem Programm vorliegt, was aber nicht der Fall sein muss. Was muss ich an dem Code noch verändern, um das gewünschte Ergebnis zu erzielen - den Speicher wieder frei zu bekommen. Gar nichts. Dein Code macht ja auch nichts außer einen Thread zu starten und wieder zu beenden. Bis auf die unnötigen bzw. falschen GC Aufrufe ist da alles in Ordnung dran. Um alles was die Freigabe der Resourcen betrifft kümmert sich der GC von alleine, da musst du normalerweise nur selber etwas machen wenn du irgendwelche unmanged Resourcen verwendest. Zitieren
NancyG Geschrieben 24. Juni 2013 Autor Geschrieben 24. Juni 2013 Ok, mal abgesehen davon, dass GC aufgerufen wird - soll an dem Code alles richtig sein! Das nehme ich erst einmal so hin. Aber!: Ich habe nach jedem Aufruf, der einen Thread erzeugt, Handles - die nicht geschlossen werden und auch Speicher, der nicht mehr freigegeben wird. Und das ist nicht richtig! @Guybrush Threepwood Auch wenn du mir jetzt wieder mit: Der GC kümmert sich darum, kommst. Es ist nicht normal - und er kümmert sich auch nicht darum. Fakt: In meiner Anwendung wird aller 250ms ein Thread erzeugt. Mittlerweile habe ich eine EXE im Taskmgr, die >1GB RAM belegt (virt: >1GB, phys: < 300MB) und der GC tut gar nichts. Ich werde das Programm so lange laufen lassen, bis der Speicher alle ist - und ich vermute, dass der GC nichts wegräumt - und es zu einer OutOfMemoryException kommt. vG Nancy Zitieren
Klotzkopp Geschrieben 24. Juni 2013 Geschrieben 24. Juni 2013 In meiner Anwendung wird aller 250ms ein Thread erzeugt.Das klingt nach einem Design-Fehler. Warum glaubst du, so viele Threads starten zu müssen? Zitieren
lbm1305 Geschrieben 24. Juni 2013 Geschrieben 24. Juni 2013 Warum nutzt nicht die ThreadPool-Klasse? ThreadPool Class (System.Threading) Zitieren
NancyG Geschrieben 25. Juni 2013 Autor Geschrieben 25. Juni 2013 (bearbeitet) @Klotzkopp Es ist kein Designfehler: Schon mal von WinCC/ODK gehört? Es werden aller 250ms Tags (Variablen) in meiner Anwendung aktualisiert. @lbm1305 Weil es nichts bringt? vG Nancy ps. Probiert es doch selber aus. C: Thread erstellen -> beenden -> Handles schliessen -> Speicher ist wieder frei! .Net:Thread erstellen -> beenden -> Handles bleiben offen -> Speicher ist nicht wieder frei! -Und der GC räumt den Dreck auch nicht weg. @lbm1305 Dein "Gefällt mir": Wofür? Bearbeitet 25. Juni 2013 von NancyG Zitieren
a3quit4s Geschrieben 25. Juni 2013 Geschrieben 25. Juni 2013 Schon mal gehoert dass managed Code fuer Echtzeitanwendungen ungeeignet ist? Zitieren
Klotzkopp Geschrieben 25. Juni 2013 Geschrieben 25. Juni 2013 Schon mal von WinCC/ODK gehört?Durchaus, ja. Es werden aller 250ms Tags (Variablen) in meiner Anwendung aktualisiert.Es ist aber totaler Blödsinn, dafür jedesmal einen neuen Thread zu starten. Zitieren
NancyG Geschrieben 25. Juni 2013 Autor Geschrieben 25. Juni 2013 Eben nicht, weil die weitere Verarbeitung der Variablen eben nicht nur so aussieht: Variablen werden in der Anwendung aktualisiert: Sondern - aller 250ms die aktuellen Variablen zBsp.: - über TCP/IP wo anders hin sollen - in ein Textfile geschrieben werden - in eine Datenbank geschrieben werden - usw. Und für jede Aktion: TCP/IP, Textfile, DB wird ein eigener Thread erzeugt. Aber das steht auch nicht zur Debatte: Fakt - nach jedem Thread der erstellt wurde, wird der Speicher nicht freigegeben. Es geht hier also nicht um die Frage: Ob das Design der AW richtig/falsch ist! Die Frage lautet, warum wird der Speicher nicht freigegeben. Und es hat auch nichts mit Echtzeit zu tun (Definition Echtzeit lesen.)! Bitte Leute!: Nehmt doch Bezug auf die Fragestellung - und sagt mir nicht, dass ich es mit C++ machen soll (was ich persönlich sowieso möchte). Oder: Dass sich um den Speicher der GC kümmert, denn dies tut er nicht. Ich habe es auch ohne die GC-Aufrufe probiert. Es bleiben Handles offen - und demzufolge auch Speicher hängen - und das bei jedem Threadaufruf! vG Nancy Zitieren
Klotzkopp Geschrieben 25. Juni 2013 Geschrieben 25. Juni 2013 Und für jede Aktion: TCP/IP, Textfile, DB wird ein eigener Thread erzeugt.Also erzeugst du alle 250 ms drei neue Threads? Das ist - ich sag's nochmal - totaler Blödsinn. Das Erzeugen eines Threads ist mit einem nicht zu vernachlässigenden Overhead verbunden. So etwas macht man nicht, damit zwingst du jedes System in die Knie. Genau dafür gibt es Threadpools. Erzeuge Job-Objekte, und lasse sie von einem Threadpool abarbeiten. Dein jetztiges System verbrät einen Großteil seiner Zeit unnötigerweise mit dem Erzeugen neuer Threads. Die Frage lautet, warum wird der Speicher nicht freigegeben.Du kommst mir vor wie jemand, der auf eine Antwort besteht, warum seine Hände bluten, wenn er sich mit der Kettensäge die Fingernägel reinigt, und sich aufregt, wenn ihm jemand rät, eine Nagelschere zu benutzten. Ich gehe davon aus, dass dein Speicherproblem verschwindet, sobald du Threads nicht mehr mißbrauchst. Zitieren
lbm1305 Geschrieben 25. Juni 2013 Geschrieben 25. Juni 2013 @lbm1305 Weil es nichts bringt? Warum nicht? Zitieren
NancyG Geschrieben 10. Juli 2013 Autor Geschrieben 10. Juli 2013 Genau dafür gibt es Threadpools. Erzeuge Job-Objekte, und lasse sie von einem Threadpool abarbeiten. Ok, kannst du mir ein kleines Beispiel dafür geben, wie es aussehen könnte? vG Nancy Zitieren
a3quit4s Geschrieben 11. Juli 2013 Geschrieben 11. Juli 2013 Wer hier so toent wird doch in der MSDN Threadpools nachschlagen koennen, oder? Zitieren
pr0gg3r Geschrieben 11. Juli 2013 Geschrieben 11. Juli 2013 Ich weiß auch nicht, ob ein Threadpool die richtige Lösung ist. Er will alle 250ms ein Thread erstellen, egal ob x Threads bereits laufen. Bei einem Threadpool werden die Threads aber ab x Stück in eine Warteschleife eingereiht und erst später gestartet. Also dann nicht nach 250ms gestartet sondern "irgendwann". Auf der anderen Seite, kann man nur bis an die Grenzen viele normale Threads starten. Und die Grenze ist ja erreicht. Zitieren
Klotzkopp Geschrieben 11. Juli 2013 Geschrieben 11. Juli 2013 Er will alle 250ms ein Thread erstellen, egal ob x Threads bereits laufen. Bei einem Threadpool werden die Threads aber ab x Stück in eine Warteschleife eingereiht und erst später gestartet. Also dann nicht nach 250ms gestartet sondern "irgendwann". Wenn die Systemleistung nicht ausreicht, um die anfallende Arbeit zeitnah zu erledigen, gibt es einen "Rückstau". Das ist aber immer noch besser, als das System durch immer neue Threads immer mehr zu belasten. Das dürfte das Problem eher noch verschärfen. Letzendlich muss dieselbe Arbeit erledigt werden, egal wie man das löst. Aber ein Threadpool erzeugt deutlich weniger Overhead, so dass mehr Leistung für die eigentliche Arbeit bleibt. Zitieren
pr0gg3r Geschrieben 11. Juli 2013 Geschrieben 11. Juli 2013 Da hast du vollkommen Recht, Klotzkopp. Ich würde aber erstmal schauen, was überhaupt gemacht wird: aller 250ms die aktuellen Variablen zBsp.: - über TCP/IP wo anders hin sollen - in ein Textfile geschrieben werden - in eine Datenbank geschrieben werden - usw. Und für jede Aktion: TCP/IP, Textfile, DB wird ein eigener Thread erzeugt. Das ist die Anforderung. Wie wäre es dann, wenn man die Daten erst einmal alle 250ms sammelt (in einem Thread mit geringer Auslastung, der innerhalbt von 250ms laufen und bestenfalls beendet werden kann) und dann bei geringer Auslastung entsprechend (auf einmal) schreibt? Ich denke, der Overhead entsteht durch die ganzen Verbindungen, zB sind Datenbankverbindungen und erst recht die Abfragen ziemlich Ressourcenfressend. Dass dies (auf Dauer) nicht unter < 250ms geschieht, ist klar, zumal die Datenbank sich meist auf einem anderen Server befindet als die Applikation. Zitieren
Klotzkopp Geschrieben 11. Juli 2013 Geschrieben 11. Juli 2013 Das ist die Anforderung.Nein, das ist bereits eine Lösung, eine Umsetzung einer Anforderungen. Ich denke, der Overhead entsteht durch die ganzen Verbindungen, zB sind Datenbankverbindungen und erst recht die Abfragen ziemlich Ressourcenfressend. Dass dies (auf Dauer) nicht unter < 250ms geschieht, ist klar, zumal die Datenbank sich meist auf einem anderen Server befindet als die Applikation.Das ist klar? Du scheinst mir eine Kristallkugel in der Tasche zu haben Zitieren
lbm1305 Geschrieben 11. Juli 2013 Geschrieben 11. Juli 2013 Wie wäre es dann, wenn man die Daten erst einmal alle 250ms sammelt Klingt ein wenig nach CQRS / EventStore. Die Frage ist, wie schnell die Daten benötigt werden, die durch die Threads verarbeitet werden sollen. Zitieren
NancyG Geschrieben 11. Juli 2013 Autor Geschrieben 11. Juli 2013 (bearbeitet) Wenn die Systemleistung nicht ausreicht, um die anfallende Arbeit zeitnah zu erledigen, gibt es einen "Rückstau". Das ist aber immer noch besser, als das System durch immer neue Threads immer mehr zu belasten. Das dürfte das Problem eher noch verschärfen.. Das Problem ist doch nicht die Leistung, sondern die Ressource (Speicher). Die 3 Threads werden schon in den 250ms abgearbeitet. Es ist sehr selten, dass mal eine Queue von 1 (aber nie höher) entsteht (Mutex WaitOne/ReleaseMutex). Ich habe es mit ThreadPoll versucht - und es bleibt das selbe Phänomen. Es starten x Threads, Handels werden erstellt, Speicher wird alloziert. Nach beenden der Threads bleiben die Handles einfach offen - und damit auch der Speicher belegt. Private Sub th() Dim i As Int32 = 0 While i < 100 i += 1 Threading.Thread.Sleep(100) End While End Sub Private Sub StartTH_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StartTH.Click For i As Int32 = 0 To 20 Step 1 Threading.ThreadPool.QueueUserWorkItem(New Threading.WaitCallback(AddressOf th)) Threading.Thread.Sleep(50) Next End Sub Wer hier so toent wird doch in der MSDN Threadpools nachschlagen koennen, oder? Kann ich - und habe ich auch gemacht. Nur leider bringt mich das eben nicht weiter. (s.o.) Bearbeitet 11. Juli 2013 von NancyG Zitieren
lbm1305 Geschrieben 11. Juli 2013 Geschrieben 11. Juli 2013 Was sollen denn die Sleeps bewirken? Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.