Ich habe vor kurzem einige .NET "Speicherlecks" (d. H. Unerwartete, verweilende GC-Root-Objekte) in einer WinForms App untersucht. Nach dem Laden und Schließen eines umfangreichen Berichts ist die Speicherbelegung auch nach einigen gen2-Sammlungen nicht wie erwartet gesunken. Unter der Annahme, dass die Berichterstattung Kontrolle wurde von einer verirrten Ereignishandler lebendig gehalten werden ich offen WinDbg geknackt, um zu sehen, was los war ....NET RegEx "Memory Leak" Untersuchung
Mit WinDbg, der !dumpheap -stat
Befehl berichtet eine große Menge an Speicher, der von String-Instanzen verbraucht wurde. Weiter verfeinernd dies mit dem !dumpheap -type System.String
Befehl Ich fand den Übeltäter, eine 90MB-Zeichenfolge für den Bericht, unter der Adresse 03be7930. Der letzte Schritt war, !gcroot 03be7930
aufzurufen, um zu sehen, welches Objekt (e) es am Leben erhalten hat.
Meine Erwartungen waren falsch - es war kein unhooked Event-Handler, der am Reporting-Steuerelement (und der Berichtszeichenfolge) hängen blieb, sondern stattdessen von einer System.Text.RegularExpressions.RegexInterpreter
Instanz, die selbst ein Nachkomme eines System.Text.RegularExpressions.CachedCodeEntry
ist. Nun ist das Caching von Regexs (etwas) allgemein bekannt, da dies hilft, den Aufwand zu reduzieren, den Regex bei jeder Verwendung neu zu kompilieren. Aber was hat das damit zu tun, meine Saite am Leben zu erhalten?
Basierend auf der Analyse mithilfe von Reflector stellt sich heraus, dass die Eingabezeichenfolge im RegexInterpreter immer dann gespeichert wird, wenn eine Regex-Methode aufgerufen wird. Der RegexInterpreter behält diese Zeichenfolge-Referenz bei, bis eine neue Zeichenfolge durch einen nachfolgenden Regex-Methodenaufruf in diese Zeichenfolge eingegeben wird. Ich würde ein ähnliches Verhalten erwarten, wenn ich an Regex.Match-Instanzen und vielleicht auch andere hängen würde. Die Kette ist so etwas wie dieses:
- Regex.Split, Regex.Match, Regex.Replace, etc
- Regex.Run
- RegexScanner.Scan (RegexScanner ist die Basisklasse, RegexInterpreter ist die oben beschriebene Unterklasse).
- Regex.Run
Die fehlbare Regex nur für die Berichterstattung verwendet wird, nur selten verwendet, und daher unwahrscheinlich, wieder verwendet werden, um den vorhandenen Bericht Zeichenfolge zu löschen. Und selbst wenn die Regex zu einem späteren Zeitpunkt verwendet würde, würde sie wahrscheinlich einen weiteren großen Bericht verarbeiten. Dies ist ein relativ bedeutendes Problem und fühlt sich einfach schmutzig an.
Alles, was ich gesagt habe, habe ich ein paar Optionen gefunden, wie man dieses Szenario lösen oder zumindest umgehen kann. Ich lasse die Gemeinde zuerst antworten und wenn keine Teilnehmer kommen, werde ich die Lücken in ein oder zwei Tagen füllen.
Verwenden Sie die Option 'Compiled', wenn Sie den Regex erstellen? –
Nein, die Option 'Compiled' wurde in diesem Fall nicht verwendet. –