Heim  >  Artikel  >  Backend-Entwicklung  >  Sechs wichtige Konzepte in .NET: Stack, Heap, Werttypen, Referenztypen, Boxing und Unboxing

Sechs wichtige Konzepte in .NET: Stack, Heap, Werttypen, Referenztypen, Boxing und Unboxing

黄舟
黄舟Original
2017-02-17 11:11:421453Durchsuche

Einführung

• Übersicht

• Wenn Sie eine Variable hinter „Was“ deklarieren passiert?
•Heap und Stack
•Werttypen und Referenztypen
•Was sind Werttypen und welche Referenztypen?
•Einpacken und Auspacken
•Leistungsprobleme beim Einpacken und Auspacken

1. Übersicht
In diesem Artikel werden sechs wichtige Konzepte erläutert: Heap , Stapel, Werttypen, Referenztypen, Boxing und Unboxing. In diesem Artikel wird zunächst erklärt, was im System passiert, wenn Sie eine Variable definieren, und dann wird der Fokus auf das Speicherduo verlagert: den Heap und den Stack. Später werden wir uns mit Werttypen und Referenztypen befassen und die wichtigen Grundlagen dieser beiden Typen erläutern.
In diesem Artikel wird ein einfacher Code verwendet, um die Auswirkungen auf die Leistung während des Ein- und Auspackvorgangs zu zeigen. Bitte lesen Sie ihn sorgfältig durch.


2. Was passiert hinter den Kulissen, wenn Sie eine Variable deklarieren?
Wenn Sie eine Variable in einer .NET-Anwendung definieren, wird ihr ein Speicherblock im RAM zugewiesen. Dieser Speicher enthält drei Dinge: den Namen der Variablen, den Datentyp der Variablen und den Wert der Variablen.
Das Obige ist eine kurze Erklärung dessen, was im Speicher passiert, aber genau welcher Art von Speicher Ihre Variable zugewiesen wird, hängt vom Datentyp ab. In .NET gibt es zwei Arten von zuweisbarem Speicher: Stack und Heap. In den nächsten Abschnitten werden wir versuchen, diese beiden Speicherarten im Detail zu verstehen.


3. Speicher-Duo: Heap und Stack

Um den Stapel und den Heap zu verstehen, lassen Sie uns anhand des folgenden Codes verstehen, was hinter den Kulissen passiert.

public void Method1()
{
// Line 1
int i=4;

// Line 2
int y=2;

//Line 3
class1 cls1 = new class1();
}

Es gibt nur drei Zeilen Code. Jetzt können wir verstehen, wie er intern Zeile für Zeile ausgeführt wird.

•Zeile 1: Wenn diese Zeile ausgeführt wird, weist der Compiler einen kleinen Teil des Speichers auf dem Stapel zu. Der Stack ist dafür verantwortlich, zu verfolgen, ob Ihre Anwendung laufenden Speicher benötigt

•Zeile 2: Der zweite Schritt wird nun ausgeführt. Wie der Name schon sagt, stapelt der Stack hier eine kleine Speicherzuweisung zusätzlich zu der gerade im ersten Schritt vorgenommenen Speicherzuweisung. Sie können sich einen Stapel als übereinander gestapelte Räume oder Kisten vorstellen. Im Stapel werden Daten über die LIFO-Logikregel (Last In First Out) zugewiesen und freigegeben. Mit anderen Worten: Das Datenelement, das zuerst in den Stapel gelangt, kann das letzte sein, das den Stapel verlässt.

•Zeile 3: In der dritten Zeile erstellen wir ein Objekt. Wenn diese Zeile ausgeführt wird, erstellt .NET einen Zeiger auf dem Stapel und das eigentliche Objekt wird in einem Speicherbereich namens „Heap“ gespeichert. Der „Heap“ überwacht den laufenden Speicher nicht, es handelt sich lediglich um eine Ansammlung von Objekten, auf die jederzeit zugegriffen werden kann. Im Gegensatz zum Stack wird der Heap für die dynamische Speicherzuweisung verwendet.

•Ein weiterer wichtiger Punkt, den es hier zu beachten gilt, ist, dass der Referenzzeiger des Objekts auf dem Stapel zugewiesen wird. Beispiel: Die Deklarationsanweisung Class1 cls1 reserviert tatsächlich keinen Speicher für die Instanz von Class1, sondern erstellt lediglich einen Referenzzeiger für die Variable cls1 auf dem Stapel (und setzt ihre Standardposition auf Null). Erst wenn das neue Schlüsselwort gefunden wird, wird Speicher für das Objekt auf dem Heap reserviert.

•Beim Verlassen dieser Methode Methode1 (der Spaß): Jetzt beginnt die Ausführungskontrollanweisung, den Methodenkörper zu verlassen. Zu diesem Zeitpunkt wird der gesamte für Variablen zugewiesene Speicherplatz auf dem Stapel gelöscht. Mit anderen Worten: Im obigen Beispiel werden alle Variablen, die sich auf den Typ int beziehen, einzeln nach dem LIFO-Prinzip „Last-In-First-Out“ aus dem Stapel entfernt.

•Es ist zu beachten, dass die Speicherblöcke im Heap zu diesem Zeitpunkt nicht freigegeben werden. Die Speicherblöcke im Heap werden später vom Garbage Collector bereinigt.


Jetzt sind viele unserer Entwicklerfreunde sicher neugierig, warum es zwei verschiedene Arten von Speicher gibt? Warum können wir nicht alle Speicherblöcke nur einem Speichertyp zuordnen?

Wenn Sie genau hinsehen, sind primitive Datentypen nicht komplex, sie enthalten lediglich Werte wie „int i = 0“. Objektdatentypen sind komplizierter; sie verweisen auf andere Objekte oder andere primitive Datentypen. Mit anderen Worten: Sie enthalten Verweise auf mehrere andere Werte und diese Werte müssen einzeln im Speicher gespeichert werden. Objekttypen erfordern dynamischen Speicher, während primitive Typen statischen Speicher erfordern. Wenn dynamischer Speicher benötigt wird, wird Speicher dafür auf dem Heap zugewiesen, andernfalls wird er auf dem Stapel zugewiesen.


4. Werttypen und Referenztypen
Da wir nun die Konzepte von Stack und Heap verstanden haben, ist es an der Zeit, Werttypen zu verstehen und Referenzen Das Konzept des Typs. Werttypen halten sowohl Daten als auch Speicher am selben Ort, während ein Referenztyp einen Zeiger auf den tatsächlichen Speicherbereich hat.
In der folgenden Abbildung sehen wir einen ganzzahligen Datentyp namens i, dessen Wert einem anderen ganzzahligen Datentyp namens j zugewiesen ist. Ihre Werte werden auf dem Stapel gespeichert.
Wenn wir einen Wert vom Typ int einem anderen Wert vom Typ int zuweisen, wird tatsächlich eine völlig andere Kopie erstellt. Mit anderen Worten: Wenn Sie den Wert des einen ändern, ändert sich der andere nicht. Daher werden solche Datentypen „Werttypen“ genannt.


Wenn wir ein Objekt erstellen und dieses Objekt einem anderen Objekt zuweisen, verweisen beide auf denselben Speicherbereich, wie im folgenden Codeausschnitt gezeigt. Wenn wir also obj obj1 zuweisen, verweisen beide auf denselben Bereich im Heap. Mit anderen Worten: Wenn wir einen von ihnen zu diesem Zeitpunkt ändern, ist der andere davon betroffen, was auch erklärt, warum sie „Referenztypen“ genannt werden.
5. Welche sind Werttypen und welche sind Referenztypen?
In .NET hängt es vollständig vom Datentyp ab, zu dem sie gehört, ob eine Variable auf dem Stapel oder im Heap gespeichert wird. Beispiel: „String“ oder „Object“ sind Referenztypen, während andere primitive .NET-Datentypen auf dem Stapel zugewiesen werden. Die folgende Abbildung zeigt im Detail, welche der .NET-Voreinstellungstypen Werttypen und welche Referenztypen sind.


6. Einpacken und Auspacken
Jetzt haben Sie bereits viele theoretische Grundlagen. Jetzt ist es an der Zeit zu verstehen, wie das oben genannte Wissen in der tatsächlichen Programmierung genutzt wird. Eine der größten Auswirkungen bei Anwendungen besteht darin, die Leistungsverbrauchsprobleme zu verstehen, die auftreten, wenn Daten vom Stapel auf den Heap verschoben werden und umgekehrt.
Betrachten Sie den folgenden Codeausschnitt. Wenn wir einen Werttyp in einen Referenztyp konvertieren, werden die Daten vom Stapel auf den Heap verschoben. Wenn wir umgekehrt einen Referenztyp in einen Werttyp konvertieren, werden die Daten auch vom Heap auf den Stapel verschoben.
Ob es vom Stapel zum Heap oder vom Heap zum Stapel verschoben wird, es wird unweigerlich einen gewissen Einfluss auf die Systemleistung haben.
Als Ergebnis entstanden zwei neue Begriffe: Wenn die Daten von einem Werttyp in einen Referenztyp konvertiert werden, wird der Vorgang als „Boxen“ bezeichnet, und der Prozess der Konvertierung von einem Referenztyp in Ein Werttyp heißt „Boxing“.


Wenn Sie den obigen Code kompilieren und ihn in ILDASM (einem IL-Dekompilierungstool) anzeigen, werden Sie im IL-Code feststellen, wie das Ein- und Auspacken aussieht wie. Die folgende Abbildung zeigt den IL-Code, der nach der Kompilierung des Beispielcodes generiert wird.


7. Leistungsprobleme beim Ein- und Auspacken
Um es herauszufinden Welche Auswirkungen haben das Ein- und Auspacken auf die Leistung? Wir führen die beiden in der Abbildung unten gezeigten Funktionsmethoden 10.000 Mal in einer Schleife aus. Die erste Methode verfügt über Boxoperationen, die andere nicht. Wir verwenden ein Stopwatch-Objekt, um den Zeitverbrauch zu überwachen.
Die Methode mit Boxing-Operation dauerte 3542 Millisekunden, während die Methode ohne Boxing-Operation nur 2477 Millisekunden dauerte, was einem Unterschied von mehr als 1 Sekunde entspricht. Darüber hinaus erhöht sich auch dieser Wert mit zunehmender Zyklenzahl. Mit anderen Worten: Wir sollten versuchen, Ein- und Auspackvorgänge zu vermeiden. Wenn Sie in einem Projekt „Boxen und Boxen“ benötigen, überlegen Sie sorgfältig, ob es sich um einen unbedingt notwendigen Vorgang handelt. Wenn nicht, versuchen Sie, ihn nicht zu verwenden.


Obwohl der obige Codeausschnitt Zeigt das Unboxing nicht an, aber der Effekt gilt auch für das Unboxing. Sie können das Unboxing implementieren, indem Sie Code schreiben und seinen Zeitverbrauch mit Stopwatch testen.

Die oben genannten sechs wichtigen Konzepte in .NET: Stack, Heap, Werttyp, Referenztyp, Boxing und Unboxing. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn). )!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn