Es scheint, als würde in jedem .net-Buch von Werttypen im Vergleich zu Referenztypen die Rede sein, und es wird darauf hingewiesen, dass (häufig fälschlicherweise) angegeben wird, wo jeder Typ gespeichert ist - der Heap oder der Stack. Normalerweise ist es in den ersten Kapiteln und als eine wichtige Tatsache dargestellt. Ich denke, es ist sogar auf Zertifizierungsprüfungen abgedeckt . Warum ist Stack vs Heap für (Anfänger-) .Net-Entwickler überhaupt wichtig? Du teilst Sachen zu und es funktioniert einfach, oder?
36
Antworten:
Ich bin davon überzeugt, dass der Hauptgrund dafür, dass diese Information als wichtig erachtet wird, die Tradition ist. In nicht verwalteten Umgebungen ist die Unterscheidung zwischen Stack und Heap wichtig, und wir müssen den von uns verwendeten Speicher manuell zuweisen und löschen. Jetzt kümmert sich die Garbage Collection um die Verwaltung, sodass sie dieses Bit ignorieren. Ich glaube nicht, dass die Nachricht wirklich durchgekommen ist, dass es uns egal ist, welcher Speichertyp verwendet wird.
Wie Fede betonte, hat Eric Lippert einige sehr interessante Dinge zu sagen: http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx .
In Anbetracht dieser Informationen können Sie meinen ersten Absatz so anpassen, dass er im Grunde lautet: "Die Leute geben diese Informationen an und gehen davon aus, dass sie wichtig sind, weil falsche oder unvollständige Informationen zusammen mit dem Erfordernis dieses Wissens in der Vergangenheit vorliegen."
Für diejenigen, die es aus Performancegründen immer noch für wichtig halten: Welche Maßnahmen würden Sie ergreifen, um etwas vom Haufen auf den Stapel zu verschieben, wenn Sie Dinge messen und herausfinden würden, dass es wichtig ist? Wahrscheinlicher ist, dass Sie für den Problembereich eine völlig andere Möglichkeit zur Leistungsverbesserung finden.
quelle
Ich stimme vollkommen zu; Ich sehe das die ganze Zeit.
Ein Grund dafür ist, dass viele Menschen aus einem C- oder C ++ - Hintergrund zu C # (oder anderen .NET-Sprachen) gekommen sind. Da diese Sprachen die Regeln zur Speicherlebensdauer für Sie nicht durchsetzen, müssen Sie diese Regeln kennen und Ihr Programm sorgfältig implementieren, um sie zu befolgen.
Nun, wohl wissend , diese Regeln und nach ihnen in C nicht erforderlich , dass Sie „den Haufen“ und „den Stapel“ zu verstehen. Wenn Sie jedoch verstehen, wie die Datenstrukturen funktionieren, ist es oft einfacher, die Regeln zu verstehen und zu befolgen.
Wenn ein Anfängerbuch geschrieben wird, ist es für einen Autor selbstverständlich, die Konzepte in derselben Reihenfolge zu erklären, in der er sie gelernt hat. Dies ist nicht unbedingt die Reihenfolge, die für den Benutzer sinnvoll ist. Ich war vor kurzem technischer Redakteur für Scott Dormans C # 4-Einsteiger-Buch, und eines der Dinge, die mir gefallen haben, war, dass Scott eine ziemlich vernünftige Reihenfolge für die Themen gewählt hat, anstatt mit den wirklich fortgeschrittenen Themen im Speichermanagement zu beginnen.
Ein weiterer Grund ist, dass auf einigen Seiten in der MSDN-Dokumentation die Überlegungen zum Speicher stark betont werden. Besonders ältere MSDN-Dokumentationen, die noch aus den Anfängen stammen. Ein Großteil dieser Dokumentation weist subtile Fehler auf, die nie beseitigt wurden, und Sie müssen sich daran erinnern, dass sie zu einer bestimmten Zeit in der Geschichte und für ein bestimmtes Publikum geschrieben wurde.
Meiner Meinung nach nicht. Viel wichtiger ist es, Dinge zu verstehen wie:
Und so weiter.
Das ist das Ideal.
Jetzt gibt es Situationen, in denen es wichtig ist. Garbage Collection ist fantastisch und relativ günstig, aber es ist nicht kostenlos. Das Kopieren kleiner Strukturen ist relativ kostengünstig, aber nicht kostenlos. Es gibt realistische Leistungsszenarien, in denen Sie die Kosten für den Erfassungsdruck gegen die Kosten für übermäßiges Kopieren abwägen müssen. In diesen Fällen ist es sehr hilfreich, die Größe, den Ort und die tatsächliche Lebensdauer aller relevanten Speicher genau zu kennen.
In ähnlicher Weise gibt es realistische Interop-Szenarien, in denen bekannt sein muss, was sich auf dem Stapel und auf dem Haufen befindet und was der Garbage Collector möglicherweise bewegt. Aus diesem Grund verfügt C # über Funktionen wie "fixed", "stackalloc" und so weiter.
Aber das sind alles fortgeschrittene Szenarien. Idealerweise muss sich ein Anfänger-Programmierer um nichts von alledem kümmern.
quelle
Ihnen allen fehlt der Sinn. Der Grund, warum die Stapel / Heap-Unterscheidung wichtig ist, liegt im Umfang .
Sobald x den Gültigkeitsbereich verlässt, ist das erstellte Objekt kategorisch verschwunden . Das liegt nur daran, dass es auf dem Stack zugewiesen ist, nicht auf dem Heap. Es gibt nichts, was an dem "..." - Teil der Methode hätte vorbeigehen können, was diese Tatsache ändern könnte. Insbesondere könnten Zuweisungen oder Methodenaufrufe nur Kopien der S-Struktur erstellt und keine neuen Verweise darauf erstellt haben, damit sie weiterleben kann.
Ganz andere Geschichte! Da sich x jetzt auf dem Heap befindet , könnte sein Objekt (dh das Objekt selbst , keine Kopie davon) sehr gut weiterleben, nachdem x den Gültigkeitsbereich verlässt. In der Tat ist der einzige Weg, wie es nicht weiterleben wird, wenn x der einzige Verweis darauf ist. Wenn Zuweisungen oder Methodenaufrufe im Teil "..." andere Referenzen erstellt haben, die zum Zeitpunkt, an dem x den Gültigkeitsbereich verlässt, noch "aktiv" sind , wird dieses Objekt weiterhin aktiv sein.
Dies ist ein sehr wichtiges Konzept, und der einzige Weg, um wirklich zu verstehen, "was und warum", besteht darin, den Unterschied zwischen Stapel- und Heap-Zuweisung zu kennen.
quelle
...
dazu führenx
, dass er in ein Feld einer vom Compiler generierten Klasse konvertiert wird und somit den angegebenen Gültigkeitsbereich überschreitet. Persönlich finde ich die Idee des impliziten Hebens unangenehm, aber Sprachdesigner scheinen sich dafür zu entscheiden (im Gegensatz dazu, dass jede Variable, die gehisst werden soll, etwas in ihrer Deklaration enthalten muss, um dies zu spezifizieren). Um die Programmkorrektheit sicherzustellen, müssen häufig alle Verweise auf ein Objekt berücksichtigt werden. Zu wissen, dass zum Zeitpunkt der Rückkehr einer Routine keine Kopie einer übergebenen Referenz verbleibt, ist nützlich.structType foo
, der Speicherortfoo
den Inhalt seiner Felder enthält; Wennfoo
sich auf dem Stapel befindet, sind dies auch die Felder. Wennfoo
es auf dem Haufen ist, sind es auch seine Felder. wennfoo
in einer vernetzten Apple II ist, so sind die Felder. Wennfoo
es sich hingegen um einen Klassentyp handelte, würde er entwedernull
einen Verweis auf ein Objekt enthalten. Die einzige Situation, in derfoo
gesagt werden kann, dass der Klassentyp die Felder des Objekts enthält, wäre, wenn es das einzige Feld einer Klasse ist und einen Verweis auf sich selbst enthält.Was das Thema betrifft, stimme ich @Kirk zu, dass es ein wichtiges Konzept ist, das Sie verstehen müssen. Je besser Sie die Mechanismen kennen, desto besser können Sie großartige Anwendungen erstellen, die reibungslos funktionieren.
Jetzt scheint Eric Lippert Ihnen zuzustimmen, dass das Thema von den meisten Autoren nicht richtig behandelt wird. Ich empfehle Ihnen, seinen Blog zu lesen, um ein umfassendes Verständnis dessen zu erlangen, was sich unter der Haube verbirgt.
quelle
Nun, ich dachte, das ist der springende Punkt bei verwalteten Umgebungen. Ich würde sogar so weit gehen, dies ein Implementierungsdetail der zugrunde liegenden Laufzeit zu nennen, über das Sie KEINE Vermutungen anstellen sollten, da es sich jederzeit ändern könnte.
Ich weiß nicht viel über .NET, aber soweit ich weiß, ist es vor der Ausführung JITted. Die JIT könnte zum Beispiel eine Escape-Analyse durchführen und was nicht, und plötzlich würden Objekte auf dem Stapel oder nur in einigen Registern liegen. Das kannst du nicht wissen.
Ich nehme an, einige Bücher behandeln es einfach, weil die Autoren ihm eine große Bedeutung beimessen, oder weil sie annehmen, dass ihr Publikum dies tut (z. B. wenn Sie ein "C # für C ++ - Programmierer" geschrieben haben, sollten Sie sich wahrscheinlich mit dem Thema befassen).
Ich denke jedoch, dass es nicht viel mehr zu sagen gibt, als "Gedächtnis wird verwaltet". Andernfalls könnten die Leute falsche Schlussfolgerungen ziehen.
quelle
Sie müssen wissen, wie die Speicherzuordnung funktioniert, um sie effizient zu nutzen, auch wenn Sie sie nicht explizit verwalten müssen. Dies gilt für so ziemlich jede Abstraktion in der Informatik.
quelle
Es kann einige Randfälle geben, in denen es einen Unterschied machen kann. Der Standard-Stack-Speicherplatz ist 1meg, während der Heap mehrere Gig beträgt. Wenn Ihre Lösung also eine große Anzahl von Objekten enthält, kann Ihnen der Stapelspeicherplatz ausgehen, während Sie über ausreichend Heapspeicherplatz verfügen.
Zum größten Teil ist es jedoch ziemlich akademisch.
quelle
Wie Sie sagen, soll C # die Speicherverwaltung abstrahieren, und Heap versus Stack-Zuweisung sind Implementierungsdetails, über die der Entwickler theoretisch nichts wissen sollte.
Das Problem ist, dass einige Dinge auf intuitive Weise nur schwer zu erklären sind, ohne auf diese Implementierungsdetails Bezug zu nehmen. Versuchen Sie, das beobachtbare Verhalten zu erklären, wenn Sie Typen mit veränderlichen Werten ändern. Es ist fast unmöglich, auf die Unterscheidung zwischen Stapel und Heap zu verzichten. Oder versuchen Sie zu erklären, warum überhaupt Wertetypen in der Sprache vorhanden sind und wann Sie sie verwenden würden? Sie müssen den Unterschied verstehen, um die Sprache zu verstehen.
Beachten Sie, dass Bücher über Python oder JavaScript nicht viel bewirken, wenn sie es überhaupt erwähnen. Dies liegt daran, dass alles entweder Heap zugewiesen oder unveränderlich ist, was bedeutet, dass die verschiedenen Kopiersemantiken niemals ins Spiel kommen. In diesen Sprachen funktioniert die Abstraktion des Gedächtnisses, in C # ist sie undicht.
quelle