Heutige PCs verfügen über eine große Menge an physischem RAM, die Stapelgröße von C # beträgt jedoch 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 hinter dieser Entscheidung .
c#
stack
clr
stack-size
Nikolay Kostov
quelle
quelle
Thread
Konstruktors verwenden. ABER das wirft die Frage auf, warum Sie einen größeren Stapel benötigen.1572864
Standardstapelgröße von Bytes (Abgerufen mit der Win32-API GetCurrentThreadStackLimits). Ich kannstackalloc
ungefähr1500000
Bytes ohne StackOverflowException.Antworten:
Sie sehen den Mann, der diese Wahl getroffen hat. David Cutler und sein Team haben ein Megabyte als Standardstapelgröße ausgewählt. Nichts mit .NET oder C # zu tun, dies wurde beim Erstellen von Windows NT festgenagelt. Ein Megabyte wird ausgewählt, wenn der EXE-Header eines Programms oder der Winapi-Aufruf CreateThread () die Stapelgröße nicht explizit angibt. Das ist der normale Weg, fast jeder Programmierer überlässt es dem Betriebssystem, die Größe auszuwählen.
Diese Wahl datiert wahrscheinlich vor dem Windows NT-Design, die Geschichte ist diesbezüglich viel zu trübe. Wäre schön, wenn Cutler ein Buch darüber schreiben würde, aber er war 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. Was MS-DOS stark beeinflusst hat.
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 anfing, sich aufzulösen, da es nicht in der Lage war, mit billiger PC-Hardware zu konkurrieren. 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 war darüber verärgert, Geld wechselte den Besitzer, um es zu regeln. Ob VMS bereits ein Megabyte ausgewählt hat, weiß ich nicht, ich kenne RSX-11 nur gut genug. Das ist nicht unwahrscheinlich.
Genug Geschichte. Ein Megabyte ist viel , ein echter Thread verbraucht selten mehr als ein paar Handvoll Kilobyte. Ein Megabyte ist also eigentlich ziemlich verschwenderisch. Es ist jedoch die Art von Verschwendung, die Sie sich bei einem bedarfsgesteuerten Betriebssystem für virtuellen Speicher leisten können, dass Megabyte nur virtueller Speicher ist . Nur Zahlen an den Prozessor, jeweils eine pro 4096 Bytes. Sie verwenden niemals den physischen Speicher, den RAM in der Maschine, bis Sie ihn tatsächlich ansprechen.
In einem .NET-Programm ist dies besonders übertrieben, da die Größe von einem Megabyte ursprünglich für native Programme ausgewählt wurde. Diese neigen dazu, große Stapelrahmen zu erstellen und Zeichenfolgen und Puffer (Arrays) ebenfalls auf dem Stapel zu speichern. Ein Pufferüberlauf, der als Malware-Angriffsvektor berüchtigt ist, kann das Programm mit Daten manipulieren. Nicht die Art und Weise, wie .NET-Programme funktionieren, Zeichenfolgen und Arrays werden auf dem GC-Heap zugewiesen und die Indizierung wird überprüft. Die einzige Möglichkeit, mit C # Speicherplatz auf dem Stapel zuzuweisen, ist das Schlüsselwort unsicheres stackalloc .
Die einzige nicht triviale Verwendung des Stacks in .NET erfolgt durch den Jitter. Es verwendet den Stapel Ihres Threads, um MSIL just-in-time zu Maschinencode zu kompilieren. Ich habe noch nie gesehen oder überprüft, wie viel Speicherplatz benötigt wird. Es hängt vielmehr von der Art des Codes ab und davon, ob der Optimierer aktiviert ist oder nicht, aber ein paar zehn Kilobyte sind eine grobe Vermutung. Ansonsten hat diese Website ihren Namen erhalten. Ein Stapelüberlauf in einem .NET-Programm ist ziemlich fatal. Es ist nicht mehr genügend Speicherplatz vorhanden (weniger als 3 Kilobyte), um noch zuverlässig 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 schreibt den Stapel eines Threads fest. Das ist ein teures Wort, das bedeutet, dass nicht nur die Größe des Stapels reserviert wird, sondern auch sichergestellt wird, dass Speicherplatz in der Auslagerungsdatei des Betriebssystems reserviert ist, sodass der Stapel bei Bedarf jederzeit ausgetauscht werden kann. Das Nicht-Festschreiben ist ein schwerwiegender Fehler und beendet ein Programm bedingungslos. Dies geschieht nur auf einem Computer mit sehr wenig RAM, auf dem viel zu viele Prozesse ausgeführt werden. Ein solcher Computer hat sich in Melasse verwandelt, bevor Programme zu sterben beginnen. Ein mögliches Problem vor mehr als 15 Jahren, nicht heute. Programmierer, die ihr Programm so
<disableCommitThreadStack>
einstellen, dass es sich wie ein F1-Rennwagen verhält , verwenden das Element in ihrer .config-Datei.Fwiw, Cutler hat nicht aufgehört, Betriebssysteme zu entwerfen. Dieses Foto wurde gemacht, während er an Azure arbeitete.
Update, ich habe festgestellt, dass .NET den Stack nicht mehr festschreibt. Ich bin mir nicht ganz sicher, wann oder warum dies passiert ist. Es ist zu lange her, seit ich es überprüft habe. Ich vermute, dass diese Designänderung irgendwo um .NET 4.5 herum stattgefunden hat. Ziemlich vernünftige Veränderung.
quelle
The only way to allocate space on the stack with C# is with the unsafe stackalloc keyword.
- Werden lokale Variablen, z. B. eineint
in einer Methode deklarierte, nicht im Stapel gespeichert? Ich glaube sie sind.maxStackSize
einen Thread ist. Ich konnte es auf [MSDN] ( msdn.microsoft.com/en-us/library/5cykbwz4(v=vs.110).aspx ) nicht finden . Basierend auf Ihren Kommentaren scheint die Stapelverwendung absolut minimal zu sein, und ich kann den kleinsten Wert verwenden, um maximal mögliche Threads aufzunehmen. Vielen Dank.Die standardmäßig reservierte Stapelgröße wird vom Linker angegeben und kann von Entwicklern durch Ändern des PE-Werts zur Verknüpfungszeit oder für einen einzelnen Thread durch Angabe des
dwStackSize
Parameters für dieCreateThread
WinAPI-Funktion überschrieben werden .Wenn Sie einen Thread erstellen, dessen anfängliche Stapelgröße 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 fragen, die Windows entworfen haben, oder warten, bis jemand von ihnen Ihre Frage beantwortet.
Wahrscheinlich weiß Mark Russinovich das und Sie können ihn kontaktieren . Vielleicht finden Sie diese Informationen in seinen Windows Internals-Büchern vor der sechsten Ausgabe, in denen weniger Informationen zu Stacks als zu seinem Artikel beschrieben werden . Oder vielleicht kennt Raymond Chen Gründe, da er interessante Dinge über Windows-Interna und deren Geschichte schreibt. Er kann Ihre Frage auch beantworten, aber Sie sollten einen Vorschlag in das Vorschlagsfeld stellen .
Aber zu diesem Zeitpunkt werde ich versuchen, einige wahrscheinliche Gründe zu erklären, warum Microsoft diese Werte mithilfe von Blogs von MSDN, Mark und Raymond ausgewählt hat.
Die Standardeinstellungen haben diese Werte wahrscheinlich, weil PCs in früheren Zeiten langsam waren und die Zuweisung von Speicher auf dem Stapel viel schneller war als die Zuweisung von Speicher auf dem Heap. Und da Stapelzuweisungen viel billiger waren, wurden sie verwendet, aber es erforderte eine größere Stapelgröße.
Der Wert war also die optimale reservierte Stapelgröße für die meisten Anwendungen. Dies ist optimal, da viele verschachtelte Aufrufe ausgeführt und Speicher auf dem Stapel zugewiesen werden können, um Strukturen an aufrufende Funktionen zu übergeben. Gleichzeitig können 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 Stapel zugewiesen sind. Wenn Sie jedoch keine Stapelzuweisungen verwenden, ist die Stapelauslastung eines Threads erheblich geringer als die Standard-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 zugewiesen.
Einige Anwendungen überschreiben den reservierten Adressraum und verpflichten sich zunächst, die Speichernutzung zu optimieren. Die maximale Stapelgröße des Threads eines nativen IIS-Prozesses beträgt beispielsweise 256 KB ( KB932909 ). Und diese Verringerung der Standardwerte wird von Microsoft empfohlen :
Quellen:
quelle