Großes string-array, wodurch die out-of-memory-Ausnahme (C#)
Ich geschrieben habe ein c# win-forms-Anwendung, die dem Benutzer ermöglicht, öffnen Sie eine log - (text -) Datei und das log-Zeilen in einem Daten-grid. Die Anwendung, die Formate, die log-Daten, so dass die Benutzer können filter, Suche etc.
Das problem, das ich habe, ist, dass, wenn der Benutzer öffnet eine log-Datei - > 300mb löst die Anwendung eine out-of-memory-Ausnahme.
Die erste app lädt alle log-Zeilen in ein string-array, dann durchläuft es die log-Zeilen, das hinzufügen von log-Eintrag-Objekte zu einer Liste.
var allLogLines = File.ReadAllLines(logPath).ToList();
var nonNullLogLines = allLogLines.Where(l => !string.IsNullOrEmpty(l));
this.ParseLogEntries(nonNullLogLines.ToArray());
Dieser erste Schritt (das laden der log-Daten in einem string-array) verwendet, die bis zu 1 GB Speicher im task-manager.
internal override void ParseLogEntries(string[] logLines)
{
this.LogEntries = new List<LogEntry>();
this.LogLinesCount = logLines.Count();
for (int i = 0; i < this.LogLinesCount; i++)
{
int entryStart = this.FindMessageCompartment(logLines, i);
int entryEnd = this.FindMessageCompartment(logLines, entryStart + 1);
int entryLength = (entryEnd - entryStart) + 1;
if (entryStart + entryLength > this.LogLinesCount)
{
entryLength = this.LogLinesCount - entryStart;
}
var logSection = new string[entryLength];
Array.Copy(logLines, entryStart, logSection, 0, entryLength);
Array.Clear(logLines, i, entryLength - 1);
this.AddLogEntry(logSection);
i = (entryEnd - 1);
}
}
Den AddLogEntry Methode addes einen log-Eintrag in der Liste (Logeinträge). Die for-Schleife verwaltet, analysiert über 50% der log-Datei, dann die out-of-memory-Ausnahme Auftritt. An diesem Punkt task-manager meldet, dass die Anwendung ist mit über 1,3 gb Speicher.
Wie Sie sehen können, habe ich oben Hinzugefügt Array.Klar, zu null aus dem Teil der log-Daten wurden erfolgreich analysiert, als Ergebnis würde ich erwarten, dass als Objekte der Auflistung Hinzugefügt wird, die Menge an Arbeitsspeicher (1 GB, um mit zu beginnen) verwendet, die durch die große log-Daten-array würde stetig reduzieren, muss es aber nicht. in der Tat ist diese Zeile macht keinen Unterschied,, um die Speicherauslastung, auch wenn ich ein GC sammeln in regelmäßigen Abständen.
Gelesen über LOH, ich gehe davon aus, dass dies ist, weil der heap wird nicht komprimiert, wie Teile der großen Auswahl wird zurückgesetzt, so dass es verwendet immer die gleiche 1 GB Speicher trotz seiner Inhalte.
Gibt es eine Möglichkeit, kann ich reduzieren die Menge an Speicher gehalten, während die Daten analysiert wird, oder eine mögliche Nacharbeit, die eventuell bessere Nutzung der Speicher? Es scheint mir seltsam, dass eine 300mb text-Datei, wenn man Sie in einem string-array, verbraucht 1 GB Speicher?
Dank.
- Was ist
FindMessageCompartment
? Auch nicht mit arrays, verwenden Sie die generischeList<string>
- irgendwelche probs zu tun ReadLine ich.e Lesen Zeile für Zeile und-Verarbeitung-Datei? Anstatt laden Sie alle auf einmal.
- Ist dies passiert, bevor Sie Ihnen zeigen, Daten, die nur während der Analyse?
- Wie Sie Lesen Sie die Datei verwenden Sie StramReader.ReadLine()?
- Welche version von .NET? .NET-4 bietet effiziente Methoden für die Datei-Zeilen auflisten, ohne das abrufen aller Zeilen in Speicher
- Seine .net 3.5. Die Datei enthält separator strings, bezeichnen das Ende der log-Abschnitt, FindMessageCompartment sucht den index der separator. Dies geschieht, bevor alle Daten angezeigt, wenn der Benutzer klickt auf "Load File".
Du musst angemeldet sein, um einen Kommentar abzugeben.
Statt Ihre Methode
ParseLogEntries(string[] logLines)
analysiert, dass alle log-Zeilen in eine zu gehen, könnten Sie stattdessen eineParseLogEntry(string logLine)
Methode, die analysiert, die aus einer einzigen Zeile.Wenn Sie kombinieren diese mit der Iteration über die Zeilen in der log-Datei ein zu einer Zeit (zum Beispiel durch erstellen Sie sich eine enumerator), dies würde vermeiden, dass die Schaffung der großen array
string[] logLines
in den ersten Platz.Eine Möglichkeit könnte wie folgt Aussehen:
Wenn Sie .NET 4.0 oder höher ist, könnte natürlich nur die Verwendung der
File.ReadLines
Methode wie bereits von der sll in einer anderen Antwort, statt der Erstellung Ihrer eigenen Methode.ReadLines
Methode ist etwas, das ich abgeholt das große Buch C# In der Tiefe, indem der große Jon Skeet 😉Ich weiß, das wird deine Frage nicht beantworten, aber möchten Sie vielleicht zu prüfen, nicht vollständig laden Sie Ihre Datei in den Speicher.
In Ihrem Fall Ihre log-Datei muss 300 MB Speicher, aber was ist, wenn es benötigt 2,5 GB?
Vor allem, wenn das Ergebnis ist eine Anzeige in einem datagrid, möchten Sie vielleicht zu verwenden paging statt, und laden Sie Sie ein kleines Stück der Daten aus der Datei jedes mal, wenn Sie es brauchen.
Saiten erfordern eine kontinuierliche Speicher-Segmente, die auf den heap; der Anwendung werfen kann "Out of Memory" einige Zeit, wenn Sie haben viele lange strings auf dem heap, und Sie versuchen, einen anderen string zuweisen, aber nicht mit segment in der benötigten Länge.
Ihre
Array.Clear
Linie kann nicht helfen, weil dielogSection
string wird nicht Müll gesammelt, in der Tat, wie die Schleife durchläuft, wird die Laufzeit haben eine schwierige Zeit, da es schwieriger ist, zu finden, ein Beispiel für 10K Platz auf dem heap, als die Suche nach 10 1K Räume.Dass ist, was dein problem ist. Als für die Lösung, im Allgemeinen würde ich Ratschläge für ein fauler Lösung. Brauchen Sie wirklich alle diese Zeichenketten im Hauptspeicher? Wenn ja, warum Sie nicht wenigstens Lesen aus einer
StreamReader
statt laden alles zustring[] logLines
?Erste, was zuerst, dass ich sehen kann, ist, dass Sie eine Wiederverwendung und verdoppelt sich der Speicherverbrauch durch die Verwendung von Aussagen wie:
Das system Lesen Sie zuerst in allen Zeilen, und dann konvertiert es in eine Liste für eine Verdoppelung der Nutzung.
Ich würde vorschlagen, Sie Lesen die Datei über ein streamreader-Verwendung:
Diese Weise wird der Speicher freigegeben, sobald Sie Weg von der Anweisung.
Auch Array.Kopie wird, um mehr Speicher verwenden, so versuchen Sie, erstellen Sie, und erstellen Sie Ihre Gewünschte Objekt in der Using-Anweisung oder stellen Ihre Objekte, die IDisposable, damit der GarbageCollector kann den Tag retten.
Ich würde vorschlagen, nicht laden, die ganze Datei in den Speicher, und verwenden Sie faul zu Lesen. Für >=
.NET 4
Sie nutzen können, Datei.ReadLines () - Methode für Lesen der Datei.