Entwickler-Ecke

Basistechnologien - List<T> System out of Memory


erther - Do 25.03.10 12:46
Titel: List<T> System out of Memory
Hallo,

ich habe folgendes Problem. Ich verwende List<T> mit T=string und lese in einer Schleife eine Datei Zeilenweise aus, die ich in die Liste speichere. Die Datei (bzw. Dateien) sind teilweise echt lang und ich bekomme System out of Memory.

Was kann ich tun? Besteht bei List<T> eine Beschränkung?

Vielen Dank für die Hilfe!
erther


bakachan - Do 25.03.10 13:12

List<T> hat soweit ich weiss keine Beschränkung was die Größe angeht also wird wohl der RAM deines Systems voll sein (wie die Fehlermeldung ja auch besagt).


erther - Do 25.03.10 13:36

Wenn ich die Elemente aus der Liste haue sobald ich sie nicht emhr brauche und dann erst neue Elemente einfüge, sollte das Problem behoben sein?!


danielf - Do 25.03.10 14:44

Hallo,

ich denke nicht, dass bei einer normale Verwendung von List<T>es zu einem System out of Memory kommt. Für sowas gibt es paging/swapping etc.
Was bedeutet das die Datei teilwese echt lang sind? Für meine Diskette sind 500kb ne Menge Holz :D

Und zeig mal bitte Code vom einlesen, ich vermute du hast da einen Programmierfehler.

Gruß


erther - Do 25.03.10 16:41

Ok, hier ein Codeschnippsel, wie ich meine Liste befülle. Mit groß meine ich so ca. 500 MB.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
List<string> testarr = new List<string>();
StreamReader sr_extrakt = new StreamReader(path);
while (sr_extrakt.Peek() >= 0)
{
  string line_out = sr_extrakt.ReadLine();
  testarr.Add(line_out);
}


Als Beispiel: Hab ca. 5,5 Mio Elemente in der Liste drin und das Programm belegt ca. 1.1 GB RAM.


Kha - Do 25.03.10 16:48

Selbst wenn das keine Unicode-Datei ist, dürfte der Speicherverbrauch nicht viel höher als 1 GB liegen :gruebel: (/edit: :D). Von wie viel Arbeitsspeicher sprechen wir denn?
Aber da an dem Code nichts auszusetzen ist:
user profile iconerther hat folgendes geschrieben Zum zitierten Posting springen:
Wenn ich die Elemente aus der Liste haue sobald ich sie nicht emhr brauche und dann erst neue Elemente einfüge, sollte das Problem behoben sein?!
Ja. Wenn auf die String-Instanzen keine Referenz mehr besteht, können sie vom GC abgeräumt werden. Und das tut er spätestens vor einer OOM-Exception.


erther - Do 25.03.10 17:42

Danke für die Diskussion!

Ich lese jetzt 500 Elemente als Block in meine Liste und entferne die nicht gebrauchten Elemente dann. Anschließend lese ich die nächsten 500 usw...

Hab mit C# (respektive der Programmierung im allgemeinen) noch nicht viel gemacht und stoße daher immer mal wieder auf teilweise Grundlagenfragen. Aber hier wird einem ja immer gut geholfen :D


Kha - Do 25.03.10 17:59

Wenn du sowieso jede Zeile einzeln behandelst, könntest du dir auch einmal IEnumerable<T> anschauen. Damit kannst du "Lazy Sequences" ermöglichen, bei denen idealerweise immer nur ein Element gleichzeitig im Speicher liegt.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
static IEnumerable<string> ReadLines(string path) // Ab 4.0: File.ReadLines
{
  using (StreamReader reader = new StreamReader(path))
    while (true) {
      string line = reader.ReadLine();
      if (line == null)
        yield break;
      yield return line;
    }
}

// Beispiel: Gesamtlänge aller Zeilen
ReadLines(path).Sum(line => line.Length);


traceurmicha - Do 01.04.10 13:23

Ich weiß das bestimmt gleich wieder einer meckert, aber ich würde das

C#-Quelltext
1:
2:
while (true) {
      string line = reader.ReadLine();

nicht so schreiben!
Wenn ich mich jetzt nicht irre erstellt er dort immer wieder eine neue Variable "line", wäre es nicht besser man würde schreiben:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
string line = null;
while(true)
{
    line = reader.ReadLine();
    .
    .
    .
}

:?: :?:


danielf - Do 01.04.10 13:39

Jepp, bekommst du ;)

Eine Variable erstellt er sowieso nicht ;) Höchstens legt er ein neues string-Objekt an.

Früher war es durchaus "wichtig" die Variable außerhalb einer Schleife zu deklarieren. Weil zur Laufzeit, wie du sagst, dann in jedem Durchlauf neuer Speicher für das Objekt allokiert wird. Mittlerweile ist es so, dass die Kompiler dies erkennen und bei der Übersetzung die Deklaration so oder so außerhalb machen.

Gruß


traceurmicha - Do 01.04.10 13:59

alles klar danielf, dan rewidiere ich meine Aussage hiermit :wink:

man lernt ebend nie aus :wink: :wink:


Kha - Do 01.04.10 21:06

user profile icontraceurmicha hat folgendes geschrieben Zum zitierten Posting springen:
wäre es nicht besser man würde schreiben
Wenn es lesbarer ist, ist es besser. Ist es das ;) ?

user profile icondanielf hat folgendes geschrieben Zum zitierten Posting springen:
Früher war es durchaus "wichtig" die Variable außerhalb einer Schleife zu deklarieren. Weil zur Laufzeit, wie du sagst, dann in jedem Durchlauf neuer Speicher für das Objekt allokiert wird.
Was hat die Deklaration mit Objekt-Allokationen zu tun? Davon gibt es doch wohl in beiden Fällen genauso viele wie Schleifendurchgänge. Die Variable selbst benötigt natürlich Stackspeicher, aber der wird ja nicht allokiert.
Du hast wahrscheinlich eher daran [http://de.wikipedia.org/wiki/Schleifeninvariante] gedacht :) .

Mal ganz abgesehen davon, dass wir uns hier in einem Iterator befinden, in dem Variablen sowieso keine Variablen bleiben :zwinker: ...