In diesem Blog konzentrieren wir uns darauf, wie die Leistung während des Softwareentwicklungsprozesses verbessert werden kann. Dies ist ein tiefgreifendes Problem im Softwareentwicklungs- oder Forschungs- und Entwicklungsprozess. In diesem Artikel wird hauptsächlich erläutert, wie Berechnungen beim Schreiben unseres Softwarecodes unter zwei Aspekten funktionieren: Speicherzuweisung und Speicherrecycling. Hier können Sie den Prozess und die Methoden der Speicherverwaltung verstehen, damit Sie darauf achten und sie bei der zukünftigen Softwareentwicklung nutzen können.
Zu den Werttypen gehören: int, float, double, bool, structure, reference, Variablen, die Objektinstanzen darstellen
Zu den Referenztypen gehören: Klassen und Arrays; weitere spezielle Referenztypen string, object
Generell gesagt: Werttypen werden auf dem Stapel gespeichert (mit Ausnahme von Werttypen, die in Referenzen enthalten sind, wie z. B. Wertfelder von Klassen, Referenzfelder in Klassen, Elemente von Arrays, die mit Referenzen in einem kontrollierten Zustand (im Heap) gespeichert werden. ; Referenztypen werden im kontrollierten Heap gespeichert. Warum es als kontrollierter Heap bezeichnet wird, wird im Folgenden ausführlich erläutert.
Ein paar Konzepte:
Virtueller Speicher: Auf einem 32-Bit-Computer verfügt jeder Prozess über 4 GB virtuellen Speicher.
Regulierter Heap (verwalteter Heap): Von wem reguliert? Der Garbage Collector ist natürlich der Garbage Collector. Wie man damit umgeht, wird weiter unten besprochen.
Nutzloser Einheitenkollektor: Neben der Komprimierung des verwalteten Heaps und der Aktualisierung der Referenzadresse verwaltet der Garbage Collector auch die Informationsliste des verwalteten Heaps usw.
Bezüglich der Speicherung von Werttypen schauen Sie sich zunächst den folgenden Code an:
{
int age=20;
double pay=2000;
}
Die beiden oben definierten Variablen int age teilen dem Compiler mit, dass er 4 Byte Speicherplatz zuweisen muss, um den Alterswert zu speichern, der auf dem Stapel gespeichert wird. Der Stapel ist eine First-in-Last-out-Datenstruktur, und der Stapel speichert Daten von Adressen höherer Ordnung bis zu Adressen niedriger Ordnung. Das Computerregister verwaltet einen Stapelzeiger, der immer auf die freie Speicherplatzadresse am Ende des Stapels zeigt. Wenn wir einen Wert vom Typ int definieren, verringert sich der Stapelzeiger um vier Bytes der Adresse, wenn die Variable den Gültigkeitsbereich verlässt , der Stapel Der Zeiger erhöht die Adresse entsprechend um vier Bytes. Er bewegt den Stapelzeiger lediglich nach oben und unten, sodass die Leistung des Stapels recht hoch ist.
Werfen wir einen Blick auf die Speicherung von Referenztypen. Schauen wir uns zuerst den Code an:
{
Customer customerA;
customerA=new Customer (); // Angenommen, die Kundeninstanz belegt 32 Bytes
}
Die erste Zeile des obigen Codes deklariert zunächst eine Kundenreferenz, der Name der Referenz ist customerA und weist Speicher zu Platz für diese Referenz auf dem Stapel. Die Größe des Speicherplatzes beträgt 4 Bytes, da er nur eine Referenz speichert. Beachten Sie, dass customerA nicht auf einen bestimmten Speicherplatz verweist Dieses Mal wird nur ein Platz zugewiesen. Das ist alles.
Während der Ausführung der zweiten Zeile durchsucht die .net-Umgebung den verwalteten Heap, um den ersten ungenutzten, kontinuierlichen 32-Byte-Speicherplatz zu finden, der der Instanz der Klasse zugewiesen ist, und stellt customerA so ein, dass er auf diesen Speicherplatz verweist Oberste Position (Heap-Speicherplatz wird von niedrig nach hoch verwendet). Wenn eine Referenzvariable den Gültigkeitsbereich verlässt, wird die Referenz im Stapel ungültig, während die Instanz im verwalteten Heap verbleibt, bis der Garbage Collector sie bereinigt.
Aufmerksame Leser haben möglicherweise einige Fragen: Muss der Computer beim Definieren eines Referenztyps den gesamten Heap durchsuchen, um einen Speicherplatz zu finden, der groß genug ist, um das Objekt zu speichern? Wird das sehr ineffizient sein? Was ist, wenn nicht genügend durchgehender Platz vorhanden ist? Hier geht es um „Sorgerecht“. Der Heap wird vom Garbage Collector verwaltet. Wenn der Garbage Collector ausgeführt wird, gibt .net alle freizugebenden Objekte frei, komprimiert andere Objekte und kombiniert dann alle freien Räume und verschiebt sie an die Spitze des Heaps, um fortlaufende Blöcke zu bilden Aktualisieren Sie sie gleichzeitig mit Verweisen auf andere mobile Objekte. Wenn eine andere Objektdefinition vorhanden ist, können Sie schnell einen geeigneten Raum finden. Es scheint, dass der verwaltete Heap ähnlich wie der Stapel funktioniert. Er weist Speicherplatz über Heap-Zeiger zu und fordert ihn zurück.
Oben wurden der Verwaltungsprozess und die Methoden der Speicherplatzzuweisung von .net besprochen. Als Nächstes sprechen wir über den Speicherrecyclingprozess. Wenn es um die Bereinigung von Ressourcen geht, müssen wir zwei Konzepte und drei Methoden nennen.
Zwei Konzepte sind: verwaltete Ressourcen und nicht verwaltete Ressourcen.
Verwaltete Ressourcen werden von der CLR (Common Language Runtime) des .net-Frameworks verwaltet; nicht verwaltete Ressourcen werden von ihr verwaltet.
Die drei Methoden sind: Finalize(), Dispose(), Close().
1. Finalize() ist eine Destruktormethode, die nicht verwaltete Ressourcen löscht.
In der Klasse definiert:
public ClassName{
~ClassName()
{
// Nicht verwaltete Ressourcen bereinigen ( Wie das Schließen von Dateien und Datenbankverbindungen usw.)
}
}
Seine Merkmale sind:
1.
Es wird vom Garbage Collector verwaltet. Wenn der Garbage Collector funktioniert, wird diese Methode aufgerufen.
2. Hoher Leistungsaufwand.
Die Funktionsweise des Garbage Collectors besteht darin, dass das Objekt, wenn es die Finalize()-Methode ausführt, es bei der ersten Ausführung in eine spezielle Warteschlange stellt Warteschlange, wenn es zum zweiten Mal ausgeführt wird. Löschen Sie dieses Objekt.
3. Die Definition und der Aufruf können nicht angezeigt werden und die Definition erfolgt in Form einer Destruktormethode.
Aufgrund der oben genannten Merkmale ist es am besten, die Finalize()-Methode nicht auszuführen, es sei denn, die Klasse benötigt sie wirklich oder wird in Kombination mit den anderen beiden Methoden verwendet.
2. Die Dispose()-Methode kann alle Ressourcen löschen, die gelöscht werden müssen, einschließlich verwalteter und nicht verwalteter Ressourcen.
ist wie folgt definiert:
public void Dispose()
{
//Reinigen Sie die Ressourcen, die bereinigt werden sollen (einschließlich verwalteter und nicht verwalteter Ressourcen)
System.GC.SuppressFinalize (this) ; //Dieser Satz ist sehr wichtig, der Grund wird unten erklärt.
}
Seine Funktionen:
1. Jeder Clientcode sollte diese Methode explizit aufrufen, um Ressourcen freizugeben.
2. Aus dem ersten Grund ist im Allgemeinen ein Backup erforderlich, und dieses Backup wird normalerweise mit der Destruktormethode abgespielt.
3. Die Klasse, die diese Methode definiert, muss die IDisposable-Schnittstelle erben.
4. Das Syntaxschlüsselwort using entspricht dem Aufruf von Dispose(). Bei Verwendung wird standardmäßig die Methode Dispose() aufgerufen. Daher müssen Klassen, die using verwenden, auch die IDisposable-Schnittstelle erben.
Die Dispose()-Methode ist flexibler und gibt Ressourcen sofort frei, wenn sie nicht mehr benötigt werden. Es handelt sich um die endgültige Entsorgung der Ressource. Der Aufruf bedeutet, dass das Objekt schließlich gelöscht wird.
3. Close()-Methode, die den Status von Ressourcen vorübergehend verwirft und möglicherweise in Zukunft verwendet wird. Verarbeitet im Allgemeinen nicht verwaltete Ressourcen.
ist wie folgt definiert:
public viod Close()
{
//Legt den Status nicht verwalteter Ressourcen fest, z. B. das Schließen von Dateien oder Datenbanken Verbindungen
}
Seine Funktionen:
Festlegen des Status nicht verwalteter Ressourcen, normalerweise Schließen von Dateien oder Datenbankverbindungen.
Schreiben Sie unten ein umfassendes und klassisches Beispiel und demonstrieren Sie mithilfe von Code die Funktion jedes Teils: (Um Ärger zu vermeiden, wurde dieses Beispiel aus dem Internet erstellt. Erklären Sie es einfach. Das ist Was meinen Sie? Ich möchte dem Autor des Codes dafür danken, dass er einen so klassischen und leicht verständlichen Code geschrieben hat.
public class ResourceHolder : System.IDisposable
{
public void Dispose()
{
Dispose(true);
System .GC.SuppressFinalize(this);
// Die obige Codezeile soll verhindern, dass der „Garbage Collector“ die Destruktormethode in dieser Klasse aufruft
// " ~ResourceHolder () „
// Warum sollten wir es verhindern? Denn wenn der Benutzer daran denkt, die Dispose()-Methode aufzurufen, dann
// „Garbage Collector“ muss nicht „unnötig“ aufgerufen werden „nicht verwaltete“ Ressource freigeben
// Wenn der Benutzer nicht daran denkt, sie aufzurufen, lassen Sie uns vom „Garbage Collector“ dabei helfen, dies „unnötig“ zu tun ^_^
// Du verstehst nicht, was ich oben gesagt habe. Es spielt keine Rolle, ich habe unten eine detailliertere Erklärung!
}
geschützte virtuelle Leere entsorgen( bool disposing)
{
if (disposing)
{
// Hier ist das Benutzercode-Snippet zum Bereinigen von „verwalteten Ressourcen“.
}
// Hier ist das Benutzercode-Snippet zum Bereinigen von „nicht verwalteten Ressourcen“. Hier ist der eigentliche Ausführungscode der Destruktormethode. Um zu verhindern, dass der Clientcode vergisst, die Dispose()-Methode aufzurufen, wurde eine Sicherungskopie erstellt.
}
~ResourceHolder()
{
Dispose(false); // Hier ist die Bereinigung „Nicht verwaltete Ressourcen“
}
}
Wenn Sie den obigen Code nicht verstehen, lesen Sie unbedingt die folgende Erklärung Vorsicht, es ist sehr klassisch. Sie werden es bereuen, wenn Sie es nicht ansehen.
Hier muss klar sein, dass der Benutzer die Methode Dispose() anstelle der Methode Dispose(bool) aufrufen muss. Die Methode, die hier tatsächlich die Freigabearbeit ausführt, ist jedoch nicht Dispose() Dispose( bool) ! Schauen Sie sich den Code genau an. Wenn der Parameter „true“ ist, müssen Sie alle verwalteten und nicht verwalteten Ressourcen bereinigen Erinnern Sie sich an das, was ich zuvor gesagt habe: „Die Destruktormethode wird verwendet, um nicht verwaltete Ressourcen freizugeben.“ Wozu dient die Destruktormethode eigentlich? der Destruktormethode ist nur ein „Backup“!
Warum?
Um es formal auszudrücken: Jede Klasse, die die Schnittstelle „IDisposable“ implementiert, muss früher oder später die Dispose()-Methode dieser Klasse aufrufen, solange der Programmierer die Objektinstanz dieser Klasse im Code verwendet gleichzeitig, wenn die Klasse enthält Für die Verwendung nicht verwalteter Ressourcen müssen die nicht verwalteten Ressourcen leider auch freigegeben werden, wenn der Code zum Freigeben nicht verwalteter Ressourcen in der Destruktormethode platziert wird (das obige Beispiel entspricht „~ResourceHolder()“ ), dann möchte der Programmierer diesen Release-Code aufrufen. Es ist unmöglich, dies zu tun (da die Destruktormethode nicht vom Benutzer aufgerufen werden kann, sondern nur vom System, genauer gesagt vom „Garbage Collector“), also sollte es jeder tun Wissen Sie, warum das obige Beispiel „nicht verwaltete Ressourcen bereinigt“ „Benutzercodesegment“ in Dispose(bool) ist, nicht in ~ResourceHolder(). Leider achten nicht alle Programmierer immer darauf, die Dispose()-Methode aufzurufen, falls dies der Fall ist Der Programmierer vergisst, diese Methode aufzurufen. Natürlich gibt es kein Problem mit verwalteten Ressourcen. Früher oder später wird es einen „Garbage Collector“ geben, der sie sammelt (es wird nur eine Weile verzögert), aber was ist mit nicht verwalteten Ressourcen? nicht unter der Kontrolle der CLR! Könnte es sein, dass die nicht verwalteten Ressourcen, die sie belegt, niemals freigegeben werden können? Wenn Sie vergessen, Dispose() aufzurufen! ruft auch die „Destruktormethode“ auf, um nicht verwaltete Ressourcen freizugeben! (Noch ein Unsinn: Wenn der Programmierer daran denkt, Dispose() aufzurufen, kann der Code „System.GC.SuppressFinalize(this);“ den „Garbage Collector“ daran hindern Aufruf der Destruktormethode, so dass keine Notwendigkeit besteht, „nicht verwaltete Ressourcen“ noch einmal freizugeben) Wir haben also keine Angst, dass Programmierer vergessen, die Dispose()-Methode aufzurufen.