Mit .NET den Fuss wegschiessen und es nicht bemerken

Mit C war es einfach, sich selber in den Fuss zu schiessen. Durch C++ wurde es erschwert, aber nicht verhindert, und es hatte schwerwiegendere Konsequenzen.

Mit C# / .NET ist es noch schwieriger, sich selbst in den Fuss zu schiessen; und falls man es tut, merkt man es noch nicht einmal.

So habe ich heute ein Problem behoben, bei welchem der Speicherverbrauch einer Anwendung (eines Services, um genauer zu sein) stetig zunahm. Natürlich habe ich als erstes überprüft, ob irgend ein Array immer mehr Objekte beinhaltet. Aber erst, als ich mit einer Testversion des ANTS Profiler von Red Gate Software den Speicherverbrauch überprüft hatte, entdeckte ich das Problem:

Ich erzeugte ein Objekt, welches einen Timer besitzt. Die Referenz auf dieses Objekt speichere ich in einem Array. Wenn das Objekt nicht mehr benötigt wird, lösche ich das Objekt aus dem Array. Es existiert keine Referenz mehr auf dieses Objekt, somit kann der Garbage Collector das Objekt aus dem Speicher entfernen.

Oder auch nicht …

Da das Objekt einen Timer enthält und sich beim Timer mit einem Eventhandler angemeldet hat, referenzieren sich das Objekt und der Timer gegenseitig. Der Garbage Collector kann somit weder Timer noch Objekt löschen und sie schwirren bis in alle Ewigkeit (bzw. bis zum Programmende), losgelöst von allen anderen Objekten, im Speicher umher.

Solche oder ähnliche Probleme entdeckte man unter Visual C++ um einiges schneller, da die Entwicklungsumgebung nicht freigegebene Objekte reklamierte, wenn man die Anwendung im Debug Modus testete. Mit dem Garbage Collector ist diese Kontrollinstanz entfernt worden, was mich in diesem Fall einiges an Zeit gekostet hat.

Ich könnte mir vorstellen, dass noch in einigen .NET-Anwendungen solche Speicherfresser vorhanden sind. Wenn es normale Desktop-Anwendungen sind, wird dies auch nicht so bald auffallen, da diese Anwendungen normalerweise nicht über mehrere Tage laufen.

Es wäre schön, wenn man, falls man dies möchte, den Garbage Collector ausschalten könnte und den Speicher wieder selber kontrollieren könnte, da man nur so sicher sein kann, dass auch alle Objekte wieder gelöscht werden. Ob mein Wunsch in einer der nächsten .NET-Framework-Versionen wohl erfüllt wird? Wahrscheinlich eher nicht.

Somit werden wir wohl mit ungeladenen Waffen umherlaufen müssen und immer wieder kontrollieren, ob wir nicht ein Loch in unserem Fuss haben…

2 Gedanken zu „Mit .NET den Fuss wegschiessen und es nicht bemerken

  1. flo

    Weshalb der delete Operator in C++ auch nicht geholfen hätte.

    Selbst „wenn man, falls man dies möchte, den Garbage Collector ausschalten könnte und den Speicher wieder selber kontrollieren könnte, da man nur so sicher sein kann, dass auch alle Objekte wieder gelöscht werden“, bestünde das Problem der sich gegenseitig referenzierenden Objekte noch immer. Explizites Löschen behebt zwar die Memory Leaks, kann aber zu Dangling Pointers führen. Beides sind aber lediglich Symptome und nicht Ursache des Hauptproblems, dem fehlerhaften Resourcenmanagements deines Programms.

    Antworten
  2. Roland Bär Beitragsautor

    Ich gebe Dir vollkommen recht, dass das Problem mein fehlerhaftes Programm, bzw. das fehlerhafte Ressourcenmanagement, war. Mit der expliziten Speicherverwaltung und der Unterstützung der Entwicklungsumgebung (wie in Visual C++) hätte ich das Problem aber schneller erkannt.
    Denn Fehler macht man immer wieder (hoffentlich nicht jedes mal die Gleichen, aber das wäre ein anderer Artikel), aber je früher man die Fehler findet, umso einfacher und kostengünstiger kann man sie beheben.

    Antworten

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert