wake-up-neo.com

Warum ist die Stapelgröße in C # genau 1 MB?

Heutige PCs haben eine große Menge an physischem RAM, aber dennoch beträgt die Stapelgröße von C # nur 1 MB für 32-Bit-Prozesse und 4 MB für 64-Bit-Prozesse ( Stapelkapazität in C # ).

Warum ist die Stapelgröße in CLR immer noch so begrenzt?

Und warum ist es genau 1 MB (4 MB) (und nicht 2 MB oder 512 KB)? Warum wurde beschlossen, diese Beträge zu verwenden?

Ich interessiere mich für Überlegungen und Gründe für diese Entscheidung .

84
Nikolay Kostov

enter image description here

Du siehst den Kerl an, der diese Wahl getroffen hat. David Cutler und sein Team haben ein Megabyte als Standardstapelgröße ausgewählt. Dies hatte nichts mit .NET oder C # zu tun und wurde bei der Erstellung von Windows NT präzisiert. Ein Megabyte ist das, was es auswählt, wenn der EXE-Header eines Programms oder der Winapi-Aufruf CreateThread () die Stapelgröße nicht explizit angibt. Wie üblich überlässt es fast jeder Programmierer dem Betriebssystem, die Größe zu bestimmen.

Diese Wahl geht wahrscheinlich dem Windows NT-Design voraus, die Geschichte ist in dieser Hinsicht viel zu düster. Wäre schön, wenn Cutler ein Buch darüber schreiben würde, aber er war noch nie Schriftsteller. Er hat die Arbeitsweise von Computern außerordentlich beeinflusst. Sein erstes Betriebssystem war RSX-11M, ein 16-Bit-Betriebssystem für DEC-Computer (Digital Equipment Corporation). Es hat Gary Kildalls CP/M, das erste anständige Betriebssystem für 8-Bit-Mikroprozessoren, stark beeinflusst. Welches MS-DOS stark beeinflusste.

Sein nächstes Design war VMS, ein Betriebssystem für 32-Bit-Prozessoren mit Unterstützung für virtuellen Speicher. Sehr erfolgreich. Sein nächstes wurde von DEC abgesagt, als das Unternehmen sich auflöste und nicht mehr mit billiger PC-Hardware konkurrieren konnte. Cue Microsoft, sie machten ihm ein Angebot, das er nicht ablehnen konnte. Viele seiner Mitarbeiter schlossen sich ebenfalls an. Sie arbeiteten an VMS v2, besser bekannt als Windows NT. DEC regte sich darüber auf, Geld wechselte den Besitzer, um es zu begleichen. Ob VMS bereits ein Megabyte ausgewählt hat, weiß ich nicht, ich kenne RSX-11 nur gut genug. Das ist nicht unwahrscheinlich.

Genug der Geschichte. Ein Megabyte ist eine Menge , ein echter Thread verbraucht selten mehr als ein paar Handvoll Kilobyte. Ein Megabyte ist also eigentlich eher verschwenderisch. Es ist jedoch die Art von Verschwendung, die Sie sich auf einem nachfrageorientierten virtuellen Speicher-Betriebssystem leisten können, dass Megabyte nur virtueller Speicher ist. Nur Zahlen an den Prozessor, eine für jeweils 4096 Bytes. Sie verwenden den physischen Speicher, das RAM im Gerät, erst dann, wenn Sie ihn tatsächlich adressieren.

Es ist in einem .NET-Programm besonders übermäßig, da die Größe von einem Megabyte ursprünglich ausgewählt wurde, um native Programme aufzunehmen. Die dazu neigen, große Stack-Frames zu erstellen und auch Strings und Puffer (Arrays) auf dem Stack zu speichern. Ein Pufferüberlauf, der als Malware-Angriffsvektor bekannt ist, kann das Programm mit Daten manipulieren. Nicht wie .NET-Programme funktionieren, werden Strings und Arrays auf dem GC-Heap zugewiesen und die Indizierung überprüft. Die einzige Möglichkeit, mit C # Speicherplatz auf dem Stapel zuzuweisen, ist das unsichere Schlüsselwort stackalloc.

Die einzige nicht triviale Verwendung des Stacks in .NET ist der Jitter. Es verwendet den Stapel Ihres Threads zum Just-in-Time-Kompilieren von MSIL zu Maschinencode. Ich habe noch nie gesehen oder überprüft, wie viel Speicherplatz erforderlich ist, es hängt eher von der Art des Codes ab und davon, ob das Optimierungsprogramm aktiviert ist oder nicht, aber ein paar Dutzend Kilobyte sind eine grobe Vermutung. Wie diese Website sonst heißt, ist ein Stapelüberlauf in einem .NET-Programm ziemlich fatal. Es ist nicht mehr genügend Speicherplatz (weniger als 3 Kilobyte) verfügbar, um weiterhin zuverlässig jeden Code zu JITEN, der versucht, die Ausnahme abzufangen. Kaboom to Desktop ist die einzige Option.

Last but not least macht ein .NET-Programm etwas ziemlich unproduktives mit dem Stack. Die CLR wird commit den Stapel eines Threads. Das ist ein teures Wort, das bedeutet, dass es nicht nur die Größe des Stapels reserviert, sondern auch sicherstellt, dass in der Auslagerungsdatei des Betriebssystems Speicherplatz reserviert ist, sodass der Stapel bei Bedarf immer ausgetauscht werden kann. Das Fehlschlagen des Commits ist ein schwerwiegender Fehler und beendet ein Programm bedingungslos. Dies geschieht nur auf Computern mit sehr wenig RAM, auf denen zu viele Prozesse ausgeführt werden. Ein solcher Computer hat sich in Melasse verwandelt, bevor die Programme zu sterben beginnen. Ein mögliches Problem vor mehr als 15 Jahren, nicht heute. Programmierer, die ihr Programm so einstellen, dass es sich wie ein F1-Rennwagen verhält, verwenden das Element <disableCommitThreadStack> in ihrer .config-Datei.

Fwiw, Cutler hat nicht aufgehört, Betriebssysteme zu entwerfen. Dieses Foto wurde gemacht, während er an Azure arbeitete.


Update, mir ist aufgefallen, dass .NET den Stack nicht mehr festschreibt. Ich bin mir nicht ganz sicher, wann oder warum das passiert ist. Es ist zu lange her, seit ich es überprüft habe. Ich vermute, dass diese Designänderung irgendwo in der Umgebung von .NET 4.5 stattgefunden hat. Ziemlich vernünftige Veränderung.

179
Hans Passant

Die standardmäßig reservierte Stapelgröße wird vom Linker angegeben und kann von Entwicklern durch Ändern des PE-Werts zum Zeitpunkt der Verknüpfung oder für einen einzelnen Thread durch Angabe des Parameters dwStackSize für die WinAPI CreateThread überschrieben werden Funktion.

Wenn Sie einen Thread mit einer Anfangsstapelgröße erstellen, die größer oder gleich der Standardstapelgröße ist, wird er auf das nächste Vielfache von 1 MB aufgerundet.

Warum entspricht der Wert 1 MB für 32-Bit-Prozesse und 4 MB für 64-Bit? Ich denke, Sie sollten Entwickler, die Windows entwickelt haben, fragen oder warten, bis jemand von ihnen Ihre Frage beantwortet.

Wahrscheinlich weiß Mark Russinovich das und Sie können ihn kontaktieren . Möglicherweise finden Sie diese Informationen in seinen Windows Internals-Büchern vor der sechsten Ausgabe, in denen weniger Informationen zu Stacks als sein Artikel beschrieben werden. Oder vielleicht kennt Raymond Chen Gründe, weil er interessante Dinge über Windows-Interna und seine Geschichte schreibt. Er kann auch Ihre Frage beantworten, aber Sie sollten einen Vorschlag in die Vorschlagbox eintragen.

Aber zu diesem Zeitpunkt werde ich versuchen, einige wahrscheinliche Gründe zu erklären, warum Microsoft diese Werte mithilfe der Blogs von MSDN, Mark's und Raymond ausgewählt hat.

Die Standardwerte haben diese Werte wahrscheinlich, weil PCs in früheren Zeiten langsam waren und die Speicherzuweisung auf dem Stapel viel schneller war als die Speicherzuweisung auf dem Heap. Und da Stapelzuweisungen viel billiger waren, wurden sie verwendet, aber es erforderte eine größere Stapelgröße.

Der Wert war also für die meisten Anwendungen die optimale reservierte Stapelgröße. Es ist optimal, weil es viele verschachtelte Aufrufe ermöglicht und Speicher auf dem Stapel zuweist, um Strukturen an aufrufende Funktionen zu übergeben. Gleichzeitig können so viele Threads erstellt werden.

Heutzutage werden diese Werte hauptsächlich aus Gründen der Abwärtskompatibilität verwendet, da Strukturen, die als Parameter an WinAPI-Funktionen übergeben werden, weiterhin auf dem Stack zugeordnet sind. Wenn Sie jedoch keine Stapelzuweisungen verwenden, ist die Stapelverwendung eines Threads erheblich geringer als die Standardeinstellung von 1 MB und es ist verschwenderisch, wie Hans Passant erwähnt hat. Um dies zu verhindern, schreibt das Betriebssystem nur die erste Seite des Stapels (4 KB) fest, wenn im PE-Header der Anwendung keine andere angegeben ist. Andere Seiten werden auf Anfrage zugeteilt.

Einige Anwendungen überschreiben den reservierten Adressraum und haben sich zunächst verpflichtet, die Speichernutzung zu optimieren. Beispielsweise beträgt die maximale Stapelgröße des Threads eines IIS nativen Prozesses 256 KB ( KB932909 ). Und diese Verringerung der Standardwerte wird von Microsoft empfohlen :

Am besten wählen Sie eine möglichst kleine Stapelgröße und legen den Stapel fest, der für einen zuverlässigen Betrieb des Fadens oder der Faser erforderlich ist. Jede Seite, die für den Stapel reserviert ist, kann nicht für andere Zwecke verwendet werden.

Quellen:

  1. Thread-Stapelgröße (Microsoft Docs)
  2. Die Grenzen von Windows erweitern: Prozesse und Threads (Mark Russinovich)
  3. Standardmäßig beträgt die maximale Stapelgröße eines Threads, der in einem systemeigenen IIS -Prozess erstellt wird, 256 KB (KB932909)
3
Yoh Deadfall