Stackoverflow beim Start des Large Collection Initializers

8

Ich baue eine Anwendung, die relativ große Tabellen verwendet, um ihre Arbeit zu erledigen ( LR-Tabellen , um genau zu sein) . Da ich sowieso Code erzeuge und die Tabelle nicht groß ist, habe ich mich dazu entschieden, meine Tabelle zu serialisieren, indem ich Code erzeuge, der die Initialisierungssyntax der C # -Ansammlung verwendet, um die Tabelle beim Start meines generierten Programms zu initialisieren:

%Vor%

Seltsamerweise stürzt die Anwendung, die ich generierte, beim Start mit einer StackOverflowException ab, wenn ich eine Tabelle mit nur ein paar hunderttausend Einträgen generierte. Der C # -Compiler kompiliert es einfach gut; Die Anwendung zur Tabellengenerierung läuft auch gut. Als ich in den Release-Modus wechselte, wurde die Anwendung tatsächlich gestartet. Eine OutOfMemoryException könnte Sinn ergeben, aber selbst dann ist die Tabelle, die ich verwende, zu klein für eine OutOfMemoryException.

Code, um dies zu reproduzieren:

Warnung : Wenn ich den folgenden Code im Veröffentlichungsmodus ausprobierte, stürzte Visual Studio 2010 für mich ab; Achten Sie darauf, ungesicherte Arbeit zu verlieren. Wenn Sie Code generieren, für den der Compiler viele Fehler generiert, wird Visual Studio außerdem hängen.

%Vor%

Führen Sie das erste Projekt für die Table.cs-Datei und dann das zweite Programm aus, um die StackOverflowException abzurufen. Beachten Sie, dass das oben genannte auf meinem Computer abstürzt: es könnte nicht auf verschiedenen Plattformen usw .; Versuchen Sie, 300000 zu erhöhen, wenn dies nicht der Fall ist.

Die Verwendung des Release-Modus anstelle des Debug-Modus scheint das Limit etwas zu erhöhen, da mein Projekt im Release-Modus nicht abstürzt. Allerdings stürzt der obige Code in beiden Modi für mich ab.

Die Verwendung von Literal int s oder string s anstelle von Tuple<int> s verursacht weder den Absturz, noch "new int ()" (aber das könnte in ein Literal 0 konvertiert werden). Die Verwendung einer Struktur mit einem einzelnen int -Feld verursacht den Absturz. Es scheint mit der Verwendung eines Konstruktors als Initialisierer verwandt zu sein.

Meine Vermutung ist, dass der Sammlungsinitialisierer irgendwie rekursiv implementiert ist, was den Stapelüberlauf erklären würde. Dies ist jedoch eine seltsame Sache, da iterative Lösungen viel einfacher und effizienter sind. Der C # -Compiler selbst hat keine Probleme mit dem Programm und kompiliert es sehr schnell (es behandelt sogar größere Sammlungen gut, aber es stürzt bei positiv großen Sammlungen wie erwartet ab).

Ich denke, es gibt wahrscheinlich eine Möglichkeit, meine Tabelle direkt in eine Binärdatei zu schreiben und dann diese Datei zu verknüpfen, aber ich habe das noch nicht angeschaut.

Ich denke, ich habe zwei Fragen: Warum passiert das oben, und wie arbeite ich daran?

Bearbeiten: einige interessante Details nach dem Zerlegen der .exe:

%Vor%

Dies deutet darauf hin, dass der Jitter tatsächlich mit einem Stack-Überlauf abstürzt, der versucht, diese Methode zu kopieren. Dennoch ist es komisch, dass es das tut, und insbesondere, dass ich eine Ausnahme davon bekomme.

    
Alex ten Brink 12.04.2012, 21:48
quelle

2 Antworten

10
  

Warum passiert das oben genannte

?

Ich vermute, dass der JIT abstürzt. Sie werden einen enormen Typinitialisierer (.cctor-Member in IL) generieren. Jeder Wert wird 5 IL-Anweisungen sein. Ich bin nicht völlig überrascht, dass ein Mitglied mit 1,5 Millionen Anweisungen Probleme verursacht ...

  

und wie arbeite ich daran herum?

Fügen Sie die Daten stattdessen in eine eingebettete Ressourcendatei ein und laden Sie sie bei Bedarf in den Typinitialisierer. Ich gehe davon aus, dass dies generierte Daten sind - also legen Sie die Daten dort hin, wo sie hingehören, und zwar in einer Binärdatei und nicht als Literalcode.

    
Jon Skeet 12.04.2012, 21:57
quelle
9

Wenn es versucht, all diese auf den Stack zu pushen, dass es eine Masse Stack-Speicherplatz benötigt, so würde ich hier eigentlich einen Stack-Overflow erwarten, je nachdem wie der Compiler ist tut es.

Nachdem ich etwas Ähnliches vorher gemacht habe (etwas, das jedes Werkzeug wie Reflektor bricht, weil die IL zu groß ist), lautet mein Ratschlag aus der Erfahrung: Mach das über die Serialisierung, nicht über c #. In meinem Fall habe ich ziemlich genau das über protobuf-net getan, d. H.

  • erzeugte das Modell (ohne Daten) als Code
  • hat es ausgeführt, um das Modell aus der Datenbank zu füllen
  • serialisiert es in eine Datei
  • hat die Datei mit meiner Bereitstellung gesendet
  • während der Initialisierung deserialisiert

Aber - ich erinnere mich, dass ich diese Diskussion kürzlich hatte; wenn es bei dir selbst war, dann stehe ich ganz bei meinen früheren Bemerkungen. Die Art, wie Sie es versuchen, ist immer noch problematisch. Der obige Ansatz (aus direkter Erfahrung) funktioniert sehr gut. Wie IL? Nicht so sehr.

Hinweis: Wenn Sie unbedingt die Datei ohne den Ausführungsschritt schreiben wollten, ist das auch möglich - nur kniffliger.

    
Marc Gravell 12.04.2012 22:00
quelle

Tags und Links