So optimiert du die Speichernutzung in C# | Mark Farragher | Skillshare

Playback-Geschwindigkeit


  • 0.5x
  • 1x (normal)
  • 1.25x
  • 1.5x
  • 2x

So optimiert du die Speichernutzung in C#

teacher avatar Mark Farragher, Microsoft Certified Trainer

Schau dir diesen Kurs und Tausende anderer Kurse an

Erhalte unbegrenzten Zugang zu allen Kursen
Lerne von Branchenführern, Ikonen und erfahrenen Experten
Eine Vielzahl von Themen, wie Illustration, Design, Fotografie, Animation und mehr

Schau dir diesen Kurs und Tausende anderer Kurse an

Erhalte unbegrenzten Zugang zu allen Kursen
Lerne von Branchenführern, Ikonen und erfahrenen Experten
Eine Vielzahl von Themen, wie Illustration, Design, Fotografie, Animation und mehr

Einheiten dieses Kurses

17 Einheiten (2 Std. 59 Min.)
    • 1. Optimierung von Speicherkapazitäten in C#

      2:37
    • 2. Einführung in die . NET

      3:44
    • 3. Was ist stack

      5:23
    • 4. Was ist Kopfzeilen?

      4:59
    • 5. Wie sind die Werttypen im Speicher hinterlegt?

      5:42
    • 6. Wie sind die reference im Speicher enthalten?

      5:29
    • 7. Was ist Boxen und boxing

      6:03
    • 8. Wie funktioniert die Garbage, in der man NET? in

      16:46
    • 9. Tipp Nr: 1: optimiere deinen Code für die Sammlung von garbage

      18:20
    • 10. Tipp 2: Vermeide die class

      17:11
    • 11. Tipp 3: verwende das Muster

      16:34
    • 12. Tipp 4: Vermeide das Boxen und Entpacken

      9:51
    • 13. Tipp 5: Füge nicht viele Strings zusammen.

      16:08
    • 14. Tipp Nr: 6: Verwenden von Strukturen anstelle von Kursen

      17:55
    • 15. Tipp Nr: 7: immer pre-allocate Kollektionen

      12:30
    • 16. Tipp Nr8: Verwirkliche dich nicht die #8: früh

      18:04
    • 17. Course

      1:44
  • --
  • Anfänger-Niveau
  • Fortgeschrittenes Niveau
  • Fortgeschrittenes Niveau
  • Alle Niveaus

Von der Community generiert

Das Niveau wird anhand der mehrheitlichen Meinung der Teilnehmer:innen bestimmt, die diesen Kurs bewertet haben. Bis das Feedback von mindestens 5 Teilnehmer:innen eingegangen ist, wird die Empfehlung der Kursleiter:innen angezeigt.

63

Teilnehmer:innen

--

Projekte

Über diesen Kurs

3bb6dc07

Moderne Computer haben viel Gedächtnis. Aber du kannst alles in Sekunden durchbrechen, wenn dein Code nicht effektiv ist, um das Erkennen und Verwenden von Speicher zu arbeiten.

Wusstest du, dass du deine code auf dich einmalen kannst, die du auf Grund der Notwendigkeit sind?

Lass dich nicht „dieser Entwickler“, der den Entwicklungsserver mit einer OutOfMemory immer wiederkehren!

Und du möchtest, du möchtest, ist nicht für die Aufhellung des hardware verantwortlich. Kannst du dir vorstellen, dein Team erklären zu lassen, dass 256 GB Speicher nicht ausreichen kann, um deinen Code auf dem Produktionsserver zu führen?

Lass mich dir helfen.

Das muss nicht so sein. Wenn du die Entstehung der Arbeit des Müllers mit der Hand bekommst, und du kannst ein paar einfache Best Practices folgen, kannst du die Speicherbedarf deines Codes draussen.

Klingen

In den letzten 10 Jahren habe ich die Geheimnisse der Müllsammlung in den Jahren von den meisten Jahren gelernt. NET, und in diesem Kurs werde ich sie alle mit dir teilen.

In einer Reihe von Kurzvorträgen werde ich dir den Prozess der Müllsammlung in der Sammlung von Mühe vorgehen. Ich zeige dir alle Probleme beim Schreiben von C#-Code wie unerwartete Boxen, duplication, die du beim Aufhören von den code, und mehr. Ich zeige dir die schnellen und einfachen Strategien, um diese Probleme zu lösen.

Am Ende dieses Kurses kannst du den of meistern können.

Warum du diesen Kurs machst?

Du solltest diesen Kurs machen, wenn du anfängst, die C#-Entwickler für Anfänger:in oder fortgeschrittene C#-Entwickler bist und deine Fähigkeiten mit der nächsten Stufe machen möchtest. Die Sammlung und das Management von Garbage können möglicherweise auch sehr kompliziert klingen – aber alle meine Vorträge sind sehr einfach. Ich erkläre alle Themen mit klarem Code und vielen ansprechenden Diagrammen Du wirst dir nicht Schwierigkeiten geben, dir mitzumachen.

Oder du arbeitest mit einem working an einem wichtigen Codeabschnitt des a und musst sicherstellen, dass deine Speichernutzung so effizient wie möglich ist? Die Tipps und Tricks in diesem Kurs hilft dir unheimlich

Oder du bereitest dich auf ein a vorbereiten? Dieser Kurs wird dir eine hervorragende Grundlage geben, um dich Fragen zu beantworten, die sie dir anwenden können.

Triff deine:n Kursleiter:in

Teacher Profile Image

Mark Farragher

Microsoft Certified Trainer

Kursleiter:in

Mark Farragher is a blogger, investor, serial entrepreneur, and the author of 11 successful Udemy courses. He has been a Founder and CTO, and has launched two startups in the Netherlands. Mark became a Microsoft Certified Trainer in 2005. Today he uses his extensive knowledge to help tech professionals with their leadership, communication, and technical skills.

Vollständiges Profil ansehen

Kursbewertungen

Erwartungen erfüllt?
    Voll und ganz!
  • 0%
  • Ja
  • 0%
  • Teils teils
  • 0%
  • Eher nicht
  • 0%
Bewertungsarchiv

Im Oktober 2018 haben wir unser Bewertungssystem aktualisiert, um das Verfahren unserer Feedback-Erhebung zu verbessern. Nachfolgend die Bewertungen, die vor diesem Update verfasst wurden.

Warum lohnt sich eine Mitgliedschaft bei Skillshare?

Nimm an prämierten Skillshare Original-Kursen teil

Jeder Kurs setzt sich aus kurzen Einheiten und praktischen Übungsprojekten zusammen

Mit deiner Mitgliedschaft unterstützt du die Kursleiter:innen bei Skillshare

Lerne von überall aus

Ob auf dem Weg zur Arbeit, zur Uni oder im Flieger - streame oder lade Kurse herunter mit der Skillshare-App und lerne, wo auch immer du möchtest.

Transkripte

1. Optimierung von Speicherkapazitäten in C#: Lassen Sie mich Ihnen eine Frage stellen. Möchten Sie ein C scharfer Performance-Architekt werden? Okay, ich muss zugeben, dass ich das Wort erfunden habe. Aber in meinem Buch ist ein C scharfer Performance-Architekt ein Senior Entwickler, der Hochleistungs-C scharfen Mantel schreibt . Also ein Senior Entwickler, der akut bewusst Off-Coat-Optimierungstechniken auf Rechte ist, rief für Spiele für die Datenanalyse für Echtzeit-Datenerfassung für all diese coolen Umgebungen, wo Fast Coat ist wichtig, Also möchten Sie Werden Sie ein C scharfer Performance-Architekt? Möchten Sie dann dem Club beitreten? Das ist der Kurs für Sie. In diesem Kurs werde ich Ihnen eine Zunge von Optimierungs-Hacks beibringen. Wir werden uns das Fundament aus der Punktnetzlaufzeit anschauen. Also werde ich dir einen Crashkurs im Stapel auf dem Heap geben. Wie Wertetypen und Referenztypen von Stores auf dem Stapel, auf dem Heap, wie Daten zwischen dem Stapel auf dem Heap verschoben werden. Wenn wir Mantel laufen, werden wir uns die Speicheroptimierung ansehen. Also werde ich Ihnen genau beibringen, wie der Garbage Collector arbeitet, auf welchen Annahmen er über Objekte, Größe und Objekt trifft über Objekte, . Leben auf, wie Sie Ihren Mantel optimieren können, indem Sie in diese Annahmen spielen, wir werden auch einige exotische Themen, wie finalisierte Stunden zu betrachten . Wir werden uns das Entsorgungsmuster ansehen. Sehen wir uns einige Kältestücke an? Das ist, dass dort zu viel Speicher verbraucht wird, viel zu gierig in der Speicherzuweisung, also lösen sie den Garbage Collector viel aus, und das verlangsamt Ihren Mantel, so dass Sie lernen, Code zu schreiben, der effizient macht ausgeschalteten Speicher verwenden. Das ist also ein ziemlich großer Kurs. Es enthält viele und viele Vorträge gibt Quiz, um Ihr Wissen auf zu testen. Sie können den Quellcode herunterladen, den ich für Benchmarking verwendet habe. Möchten Sie ein C Sharp Performance Architects werden? Dann ist dies der Kurs für Sie. Also habe ich diesen Kurs für mittelständische oder Senioren-Shop-Entwickler erstellt, die lernen wollen , wie man schnell und effizient C scharfen Mantel schreibt, um ihre Karriere für coole Themen wie Spielentwicklungen oder Echtzeit-Datenerfassung vorzubereiten . Danke, dass Sie zugehört haben. Ich freue mich, Sie im Kurs begrüßen zu dürfen 2. Einführung in die . NET: Werfen wir einen Blick auf ein typisches Meeresprogramm. Das Programm wird aus Klassen bestehen, die Felder und Methoden enthalten. Eine typische Methode beginnt mit Song-Wertsachen, die auf initialisiert deklariert werden. Dann gibt es Codes, um irgendeine Art von Aktion auszuführen. Und schließlich endet die Nachricht. Die Variablen in den Methoden werden im Stapel und jedes Mal zugewiesen, wenn Sie die neuen Schlüsselwörter für Objekte sehen , die im Heap zugeordnet sind. Aber hier ist eine Frage an Sie. Was ist das Gegenteil? Aus Neu, Welche Schlüsselwörter löscht auf Objekten Einige Sprachen haben tatsächlich ein spezielles Schlüsselwort namens delete, das das Gegenteil von new ist, löscht explizit ein Objekt aus dem Heap, aber das Punktnetz-Framework nicht Habe es. Es gibt ein neues Schlüsselwort, aber es gibt keine kostenlosen. Löscht Schlüsselwörter. Wie ist das möglich? Die Antwort ist, dass es einen speziellen Prozess namens Garbage Collector gibt, der kontinuierlich Objekte löscht, die von Ihrem Mantel nicht mehr verwendet werden. Sie fragen sich vielleicht, warum Microsoft sich für die Implementierung einer so komplizierten Lösung entschieden hat. Warum nicht einfach die Leads-Schlüsselwörter zur C-scharfen Sprache hinzugefügt und vermeidet die Notwendigkeit eines Garbage Collectors insgesamt? Der Grund, sie den Garbage Collector hinzugefügt haben, ist sehr einfach in Sprachen, die auf den löschenden Schlüsselwörtern angewiesen sind. Die häufigsten Programmierfehler sind auf die Anfrage zurückzuführen. Juice off New und Elise lokalisieren ein Objekt mit neuen, aber vergessen, es zu löschen. Dies wird als Speicherleck bezeichnet, wobei ein Objekt verwendet wird, nachdem es bereits gelöscht wurde. Dies führt zu Speicher, Beschädigung und Löschen eines Objekts, das nicht zugewiesen wurde. Dies verursacht im Speicher Fehler durch Hinzufügen eines Garbage Collectors zum dot net Framework, Microsoft hat diese Programmierfehler vollständig beseitigt. Es ist sehr schwer, nicht unmöglich. Mind you zwei erstellt die Speicherliga in C scharf oder korrupte Programmspeicher. Aber ein Garbage Collector hat auch Nachteile, da die Hälfte der Speicherverwaltung jetzt vollautomatisch ist . Entwickler verlieren tendenziell die Speicher-Footprints von ihren Codes aus den Augen, und dies kann zu Codes führen, die so funktionieren, wie es soll, aber Tausende Male mehr Speicher zuweisen , als es tatsächlich benötigt. Ich werde Ihnen zeigen, wie Sie die Speicherzuweisungen von Ihren Codes messen können. Ich werde Ihnen genau beibringen, wie der Garbage Collector funktioniert, und ich werde einige einfache Tricks mit Ihnen teilen, die Ihnen helfen, den Speicherbedarf von Ihren eigenen Codes drastisch zu reduzieren . 3. Was ist stack: Stack-Speicher war einfach der Stack ist ein Block-Off-Speicher, der zum Aufrufen von Methoden und Speichern lokaler Variablen verwendet wird . Lassen Sie mich das hier auf die Tafel zeichnen. Ich werde den Stapel als vertikale Spalte wie folgt zeichnen. Nein, wenn ich mit dem Ausführen von Code beginne, wird dieser Stack zunächst leer sein. Aber wenn Mike Oates die Methoden aufruft, die dies geschieht, die Methodenparameter die Rückgabeadresse für alle lokalen Variablen von den Methoden auf den Stack gesetzt . Dieser ganze Datenblock wird als Stack-Frame bezeichnet. Was passiert also mit dem Stack, wenn meine Codes eine andere Methode innerhalb dieser Methode aufrufen, was dasselbe wieder passiert? Die Methodenparameter geben Adresse für lokale Variablen von der neuen Methode zurück, die ich auf den Stack direkt neben dem vorherigen Stack-Frame setze . Deshalb wird es ein Stack genannt. Die Informationen werden übereinander gestapelt. Was passiert, wenn mein Kult auf return statement trifft? Wie Sie wahrscheinlich wissen, beendet eine return-Anweisung eine Methode und kehrt zum aufrufenden Code zurück. Jetzt auf dem Stapel. Das ist es, was passiert. Der gesamte Stack-Frame, der der Methode entsprach, wird entfernt, aber Sie denken vielleicht, was mit allen lokalen Variablen passiert ist, die im Stack-Frame gespeichert wurden ? Nun, sie gehen alle aus dem Geltungsbereich, was nur eine schicke Art ist, zu sagen, dass sie zerstört sind. Dies ist also eine wichtige Tatsache, um sich an den Moment zu erinnern, in dem Sie alle Ihre lokalen Variablen aus den Methoden zurückkehren , dass Methoden außerhalb des Geltungsbereichs gehen und zerstört werden. Wenn ich mit meinem Programm fortfahre und auch aus den ersten Methoden zurückkehre, sind wir wieder dorthin, wo wir mit einem leeren Stack begonnen haben. Sie fragen sich vielleicht, was passiert, wenn eine Methode andere Methoden aufruft, die eine andere Methode verursachen, die 1000 Mal gut andere Methoden aufruft. Der Stack würde sich schnell mit Stack-Frames füllen, bis normaler Raum vorhanden ist. Schließlich wird der Bestand vollständig auf dem DOT-Netz-Framework Throws-Stack voll sein. Überlauf-Ausnahme. Wenn Sie diese Fehlermeldung sehen, bedeutet dies, dass Sie irgendwo in Ihrem Code eine unendliche Sequenz-Off-Methodenaufrufe haben. Werfen wir einen Blick auf einen Code. Ich habe ein einfaches Programm geschrieben, um ein Quadrat auf dem Bildschirm zu zeichnen. Sie sehen, dass ich eine Schublade quadratische Methoden, die eine Schubladenlinie Methode viermal aufruft, um die vier Seiten von einem Quadrat zu zeichnen. Ich werde eine Pause setzen, Punkte in der Schublade, Linienmethoden und dann meine Kälte laufen. Sieh dir das an. Nun, an diesem Punkt in meinem Mantel, wird dieser Stapel so aussehen. Mein erster Aufruf, Quadrat zu zeichnen, ist hier am unteren Rand des Stacks mit seinen vier Parametern, Rückgabeadresse und lokalen Variablen. Als nächstes kommt der Aufruf in eine Schubladenlinie mit erneut vier Parametern. Rücksendeadresse am. In diesem Fall keine lokalen Variablen, da Schubladenlinie keine in Visual Studio hat. Sie können einen Blick auf diesen Stapel werfen, indem Sie die Call-Stack-Ansicht öffnen, die hier ist. Sie können den Aufruf in ziehen Quadrat und dann in eine Schubladenlinie sehen. Dieses Fenster zeigt Ihnen also an, welche Stack-Frames auf dem Stapel gespeichert sind. Als abschließende Demonstration zeige ich Ihnen einen Stapel. Überlauf-Ausnahme. Lassen Sie mich meinen Mantel so modifizieren. Ich habe meinen Mantel modifiziert. So zeichnen Sie nun eine Linie Anrufe in eine Schubladenlinie, die dann in eine Schubladenlinie ruft, die in eine Schubladenlinie ruft. Du kriegst das Bild. Diese Sequenz wird nie enden. Wenn ich dieses Programm ausführe, wird es eine unendliche Sequenz von gezeichneten Linien Methoden erstellen, die sich selbst nennen Lassen Sie uns das Programm ausführen und sehen, was passiert. Ich bin da, du hast es. Der Stapel ist in der Größe begrenzt. Wenn ich zu viele Methoden habe, die alle Methoden aufrufen, wird der Stack schließlich vollständig auf dem DOT-Net-Framework die Stack-Überlaufausnahme voll sein. Also, was haben wir gelernt? Das wird Net Framework verwendet den Stack, um Medikamente jedes Mal zu verfolgen, wenn Sie die Nachricht alle Methodenparameter aufrufen . Die Rückgabeadresse für alle lokalen Variablen wird auf dem Stapel platziert. Dieser gesamte Speicherblock wird als Stack-Frame bezeichnet. Wenn Sie aus einer Methode zurückkehren, der oberste Stack-Frame entfernt. Alle lokalen Variablen gehen an dieser Stelle aus dem Geltungsbereich, und ich habe zerstört. Wenn Sie eine unendliche Sequenz Off-Methoden haben, wird der Aufruf aller Methoden, die der Bestand vollständig gefüllt, bis Brennnessel einen Stapel wirft. Überlauf-Ausnahme 4. Was ist Kopfzeilen?: die andere Art von Speicher in einem Computer wird als Trefferspeicher oder einfach der Heap bezeichnet. Werfen wir einen Blick auf die folgende Codezeile jetzt. Jedes Mal, wenn die Schlüsselwörter new online angezeigt werden, haben Sie ein Objekt auf dem Heap erstellt. Das ist eine Grundregel. Objekte für Innennetze werden immer auf dem Heap erstellt. Sie gehen nie auf den Stapel. Also habe ich einige Änderungen an meinem Schubladen-Quadrat-Programm vorgenommen. Werfen wir einen Blick. Zuvor wurden die Schubladenlinien Methoden erwartet. Vier haben Ihre Parameter beigesetzt, um eine Linie zu zeichnen. Aber jetzt habe ich eine Schublade Polygon Methods, die ein Array Offline-Objekte erwartet, das alles auf einmal zeichnet. Die Draw square Methode richtet ein Linienfeld mit vier Objekten, die den vier Seiten aus dem Quadrat entsprechen , und ruft dann Drop all a gun auf, um alles in einem zu zeichnen. Gehen Sie jetzt Denken Sie daran, in meinem alten Schubladen-Quadrat-Programm, Ich legte einen Bruchpunkt innerhalb der Schubladenlinie Methoden, und wenn ich meine Codes ausgeführt, sah der Stapel so aus. Aber jetzt habe ich viele Änderungen am Programm vorgenommen. Also, wie wird der Stapel auf dem Heap jetzt aussehen? Okay, also stell dir vor, ich habe einen Bruchpunkt in die Schublade gelegt. Polygon-Methoden beim Ausführen meines Programms. Der Stapel auf dem Heap würde dann so aussehen. Die Parameter kalten Linien existieren auf dem Stapel, weil es ein Parameter ist, aber ich habe es mit den neuen Schlüsselwörtern initialisiert. Das Array selbst wird also auf dem Heap erstellt, so dass die Variable auf diesem Stack auf ein Objekt auf dem Heap verweist. Sie können auch sehen, dass das Linien-Array vier Elemente hat und dass jedes Element auf ein Linienobjekt an anderer Stelle auf dem einen verweist . Was passiert nun, wenn die Methode zum Zeichnen von Polygonen beendet ist? Dieser Stapelrahmen wird auf den Lichtern entfernt, Parameter geht aus dem Bereich und wird zerstört. Aber hier ist etwas, das Sie vielleicht nicht erwartet haben. Das Array in der Zeile Objekte auf dem Heap ist jetzt weiterhin vorhanden. Das ist eine interessante Situation. Der Löwenparameter ist außerhalb des Geltungsbereichs, aber die Objekte auf der Hitze sind immer noch da. Wir sagen, dass die Objekte aufgrund der Variablen oder Parameter, die auf sie ohne Umfang verweisen, de referenziert werden. De referenzierte Objekte existieren weiterhin und werden nicht sofort zerstört. Also hier ist ein weiterer wichtiger Take away das Punkt-Net-Framework wird immer die Bereinigung der referenzierten Objekte verschieben , und das liegt daran, dass das Bereinigen des Heaps viel Zeit in Anspruch nimmt, indem es so lange wie möglich verschoben wird. Ihr Code wird tatsächlich schneller ausgeführt, aber schließlich muss das Framework den Heap säubern, sonst würde uns schnell der Speicher ausgehen . Dieser Reinigungsprozess wird Garbage Collection genannt, und es passiert regelmäßig im Hintergrund, wenn das Framework Garbage Collection startet . Es identifiziert alle Objekte auf dem Heap, die nicht mehr durch einen variablen Parameter oder Objekte in Ihrem Mantel referenziert werden, auf dessen De weist jedem von ihnen zu. Also, was haben wir gelernt? Jedes Mal, wenn Sie die neuen Schlüsselwörter in Ihren Codes verwendet haben, erstellen Sie ein Objekt auf dem Heap. Die Variable selbst kann auf dem Stack leben, aber sie bezieht sich auf ein Objekt auf dem Heap, wenn Parameter und lokale Variablen auf dem Stapel außerhalb des Gültigkeitsbereichs liegen. Die entsprechenden Objekte aus dem Heap bin ich nicht zerstört. Sie existieren weiterhin in einem D-referenzierten Zustand. Das Net Framework verschiebt die Bereinigung der referenzierten Objekte auf dem Heap aus Performance-Gründen so lange wie möglich. Aber schließlich startet das Framework einen Prozess namens Garbage Collection. Da de alle referenzierten Objekte auf der Hüfte zuweist 5. Wie sind die Werttypen im Speicher hinterlegt?: in der vorherigen Vorlesung haben wir jetzt über diesen Stapel auf dem Heap erfahren. Zuvor, als ich über diesen Stapel sprach, zeigte ich etwas Code mit der Schubladenlinie Chaos. Es ist, dass vier ganzzahlige Parameter verwendet. Werfen wir einen genaueren Blick auf eine ganze Zahl im Dark Net-Framework. Der aufständische Typ ist Teil einer speziellen Klasse von Typen, die Werttypen genannt werden, aber was ist der Werttyp? Der Werttyp ist ein Variablentyp, bei dem der Typ und der Wert aus der Variablen zusammen verzerren. Wenn ich also eine lokale Integer-Variable mit einem Wert von 12 Hunderten und 34 habe, wird dieser Interview-Typ auf seinem Wert zusammen so gespeichert. Werfen wir einen Blick auf diesen Stapel noch einmal. Zuvor, als ich über diesen Stack sprach, erwähnte ich alle Arten von Daten, die auf dem Lager gespeichert sind. Sie sind Nachrichtenparameter. Die Rücksendeadresse aus einer Nachricht ertönt lokale Variablen. Wenn ich also eine lokale Variable aus dem Typ in zog mit dem Wert von 1234 habe, würde sie so auf dem Stack gespeichert werden . Sie sehen, dass der Typ und der Wert, der zusammen auf dem Stapel gespeichert ist, dies jetzt im Hinterkopf behalten , denn in der nächsten Vorlesung werde ich über Referenztypen sprechen, was Speicher anders ist. Sie fragen sich also vielleicht, welche Typen im dunklen Net-Framework tatsächlich Werttypen sind. Nun, hier ist eine vollständige Liste. Alle numerischen Typen sind Werttypen, einschließlich aller Ganzzahl-Gleitkommastellen auf Dezimaltypen. auch Boolescher Wert in Operationen und Strukturen sindauchWerttypen. Alles andere im Punktnetz-Framework wird als Referenztyp bezeichnet , den ich in Kürze besprechen werde. Wenn Benutzer versuchen, den Unterschied zwischen einem Werttyp und einem Referenztyp zu erklären, hören Sie oft die folgende Erklärung. Ein Werttyp ist ein Typ, der auf dem Stapel vorhanden ist. jedoch Dies istjedochRaumwert. Typen können sowohl auf dem Stapel auf dem Yeah existieren, lassen Sie mich demonstrieren. Zuvor, als ich über den Heap sprach, erwähnte ich, welche Art von Daten auf dem Heap gespeichert sind. Erinnern Sie sich, was es waas? Es waas alle Objekte Instanzen, die mit einem neuen Schlüsselwort in C scharf erstellt wurden. Stellen Sie sich also vor, dass ich ein Objekt auf dem Heap mit den neuen Schlüsselwörtern in meinem Mantel auf diesem Objekt erstelle, hat ein Feld mit dem Wert 12 Hunderte 34. Jetzt wird diese ganze Zahl so gespeichert. Sie sehen also, ich weiß, dass ich den Werttyp auf dem Heap habe, so dass Werttypen sowohl auf dem Stapel am Tag existieren können die definierenden Eigenschaftswerttypen nicht dort sind, wo sie gespeichert werden, sondern dass der Wert zusammen mit dem Typ gespeichert wird. Lassen Sie mich zum Abschluss bringen, indem Sie zusätzliche Features oder Werttypen Wichtigkeit zeigen. Nehmen wir an, ich habe eine Nachricht in meinen Codes mit zwei Variablen A und B. Beide sind eingeführt. Die Variable A enthält den Wert 1234 unter Variable B ist Null. Was passiert, wenn ich es zuweise, wird Check this out sein. Der Wert von A wird in be kopiert. Nun ist dies ein wichtiges Merkmal der Wertetypen ihre Vorzeichen nach Wert, was bedeutet, dass ihr Wert kopiert wird. Also, was passiert jetzt, wenn ich A und B vergleiche? Sie sind zwei verschiedene Variablen, die nur zufällig den gleichen Wert enthalten. Wie wird das Punktnetz-Framework die Situation so gut interpretieren? Das Framework betrachtet diese beiden Variablen als gleich. Dies ist die zweite wichtige Funktion aus Werttypen. Sie werden nach Wert verglichen, was bedeutet, dass zwei verschiedene Variablen, die den gleichen Wert enthalten, als gleich angesehen werden. Also, was haben wir gelernt? Werttypen speichern ihren Wert direkt zusammen mit dem Typwert. Typen können auf dem Stapel vorhanden sein und glauben, dass Werttypen ein Zeichen nach Wert sind, was bedeutet, dass der Wert über Wert kopiert wird. Typen werden nach Wert verglichen. Zwei Variablen mit demselben Wert werden als gleich angesehen. 6. Wie sind die reference im Speicher enthalten?: In der vorherigen Vorlesung sprach ich über die Wertarten und erwähnte kurz sein Gegenstück den Referenztyp. Aber was ist ein Referenztyp? Nun, ein Referenztyp ist ein Variablentyp, der sich auf einen Wert bezieht, der auf dem Heap gespeichert ist. Vorher nicht. Als ich über den Heap sprach, zeigte ich mein modifiziertes Zeichenquadrat Programm, das eine Polygon-Methode zeichnen hatte, wenn Sie sich mit einem Linienarray-Parameter erinnern, So zeichnen Polygon ein Array Offline-Objekte erwartet. Werfen wir einen genaueren Blick auf diese Linienobjekte, nur um Ihren Speicher zu aktualisieren. Hier ist wieder mein Mantel. Sie können die Definition außerhalb der Line-Klasse hier sehen. Es ist ein einfacher Datencontainer mit zwei Sätzen von Koordinaten. Stellen Sie sich also vor, ich habe eine Nachricht mit einer lokalen Variablen außerhalb der Typzeile. Wie würde das aussehen? Ein Gedächtnisbrunnen, wie dieser. Sie können sehen, dass sich die Variable selbst auf dem Stapel befindet, aber sie bezieht sich auf eine Zeile Objekte auf dem, aber kann eine Referenztypvariable auch auf dem Heap existieren ? Ja, sicher. Alles, was ich tun muss, ist ein Objekt auf dem Heap zu erstellen. Die Verwendung der neuen Schlüsselwörter hat die Hälfte gedreht, dass Objekte ein Feld außerhalb der Typzeile haben. Nun wird die Erinnerung dann so aussehen. Sie sehen, dass sie jetzt eine Referenztypvariable auf dem Heap haben, und es bezieht sich auf eine Linie Objekte, die auch auf dem was an anderer Stelle gespeichert ist, so zusammenzufassen. Referenztypen können auf dem Stapel am Tag auf dem Heap vorhanden sein, genau wie Werttypen, aber sie beziehen sich immer auf einen Wert auf dem Heap. Lassen Sie mich abschließend zeigen, dass Sie zusätzliche Funktionen von Referenztypen wichtig sind. Nehmen wir an, ich habe eine Nachricht in meinen Codes mit zwei Variablen A und B. Beide Variablen sind Zeilen. Die Variable A bezieht sich auf eine Zeileninstanz auf dem Heap unter Variable B wird gesagt, um zu wissen. Nun, was passiert, wenn ich A zu B. überprüfe, wird die Referenz von A in sein kopiert. Dies ist ein wichtiges Merkmal von Referenztypen. Sie sind Zuordnungen durch Referenz, was bedeutet, dass die Referenz kopiert wird. Sie enden mit zwei Variablen, auf die gleiche Objektinstanz in der Hüfte beziehen. Also, was passiert jetzt, wenn ich A und B vergleiche? Es handelt sich um zwei verschiedene Variablen, die sich auf dieselben Objekte auf dem Heap beziehen. Wie wird das Punktnetz-Framework diese Situation gut so interpretieren? Das Framework betrachtet diese beiden Variablen als gleich. Aber warten Sie, was ist mit diesem Szenario, um Typvariablen zu referenzieren, die auf zwei separate Objekte auf dem Heap verweisen . Beide Objekte enthielten jedoch identische Daten. Wie wird das Punktnetz-Framework diese Situation interpretieren, die Drinker klingt. Das Framework betrachtet diese beiden Variablen als nicht gleich, so dass dies ein weiteres wichtiges Merkmal von Referenztypen ist. Sie werden durch Referenz verglichen, was bedeutet, dass zwei verschiedene Variablen, die sich auf dieselben Objekte beziehen, als gleich angesehen werden. Aber zwei verschiedene Variablen, auf zwei separate, aber identische Objekte beziehen, gelten als ungleich. Also, was haben wir gelernt? Referenzzeiten können auf den No-Value-Speicher Reference Types erkannt werden, ein Verweis auf ihren Wert, und dieser Wert wird immer auf der Referenz gespeichert. Typen können auf diesem Stapel am Tag am vorhanden sein, aber ihr Wert wird immer in der Referenz gespeichert. Typen sind Zuweisungen durch Referenz, was bedeutet, dass die Referenz über Referenz kopiert wird. Typen werden als Referenz verglichen. Zwei Variablen, die sich auf dieselben Objekte beziehen, werden als gleich angesehen, und zwei Variablen, die sich auf separate, aber identische Objekte beziehen, werden als ungleich betrachtet 7. Was ist Boxen und boxing: In diesem Vortrag werde ich Ihnen ein Rätsel zeigen. Werfen Sie einen Blick auf diesen Mantel. Dies ist ein sehr einfaches Programm. Ich habe mit der Variablen A begonnen, die den Wert 1234 enthält. Dann deklariere ich eine zweite Variable. Seien Sie von Typobjekten und ich gebe a zu B in C scharf alle Zeiten erben von Objekten, einschließlich Ganzzahlen, so dass Sie grundsätzlich alles in eine Objekttypvariable einfügen können. Aber warten Inter Juroren sind Werttypen, und Objekte sind referenzierte Typen. Also im Speicher werden meine Variablen so gespeichert. Hier ist meine ganzzahlige Variable A mit seinem Wert von 1234. Und hier ist meine Objektvariable B. Aber B ist ein Referenztyp, und wir haben in der vorherigen Vorlesung gelernt, dass Referenztypen hier immer auf einen Wert auf dem Heap verweisen . Dies ist nicht möglich, da A eine lokale Variable ist und daher am Tag auf dem Stapel vorhanden ist. Es ist ein Werttyp, daher wird sein Wert auch auf dem Stapel gestartet. Es gibt einfach keine Möglichkeit für B, sich auf a zu beziehen, da beide Variablen in verschiedenen Speichertypen rezitiert . Ein Referenztyp kann sich nie auf den Stack-Speicher beziehen, so dass dieses Programm nie funktionieren kann. Richtig? Nun, das hier tut es. Ich gehe zurück zum Dezember im Studio Andi, führe das Programm. Los geht's. Cool. Es funktioniert tatsächlich. Aber wie ist das möglich? Nun, das ist komisch. Basierend auf dem, was wir in früheren Vorlesungen gelernt haben, sollte dieses Programm nicht funktionieren. Aber doch tut es das. Wie kann man das herausfinden? Lassen Sie mich dieses Programm in Zwischensprache kompilieren. Indem wir die Zwischensprachenanweisungen untersuchen, könnten wir einen Hinweis finden. Und hier ist es. Sieh dir das an. Fortgeschrittene Sprachunterricht genannt Bücher. Kannst du erraten, was es hier macht? Ich ziehe an die Tafel. Hier sind die Speicherlayouts mit den beiden Variablen A und B. Nun tut die Buchanweisung dies. Damit das Programm funktioniert, kopiert das Net-Framework tatsächlich den Interviewer-Wert aus dem Stapel, dann besteuert den Wert in Objekte darauf platziert diese Objekte auf dem, so dass die Variable B dann auf seine verweisen kann . Dieser ganze Prozess wird Boxen genannt. Boxen findet jedes Mal hinter den Kulissen statt. Wenn Sie eine Variable Parameter Felder oder Eigenschaft off Zeit Objekte und Sie einen Werttyp zu seinem Boxen zuweisen ist schön, weil es Art der Unschärfe der Linie zwischen Wertzeiten und Referenztypen. Aber Boxen kann auch ein Schmerz sein, weil es zusätzliche Gemeinkosten in Ihrem Code einführt. Jetzt fragen Sie sich vielleicht, ob es einen entsprechenden Prozess namens Unboxing gibt. Ja, das gibt es. Hier ist wieder mein Programm. Werfen wir einen Blick auf die letzte Zeile. Ich deklariere eine Variable, siehe Typ Integer und weisen den Objektwert mit einem Typecast zu. Noch ein bisschen Magie. Eigentlich, weil siehe, existiert auf dem Stapel und sein die Objekte bezieht sich auf ein Objekt auf die in der Zwischensprache . Die entsprechende Anweisung ist hier. Es heißt Unbox. Lassen Sie mich zurück zur Tafel auf Draw the Unboxing Prozess. Wir begannen von der Box-Situation mit der Ganzzahl auf der Now. Dann passiert das. Unboxing entpackt die ganze Zahl auf dem Heat und kopiert den Wert zurück in die Variable Siehe auf dem Stapel. Entpacken erfolgt jedes Mal hinter den Kulissen, wenn Sie einen Objektwert haben und ihn in einen Werttyp umwandeln . Also, was haben wir gelernt? Boxing ist der Prozess, Werttypen auf dem Stapel zu nehmen und sie in Objekte zu packen, um diese Objekte auf dem Heap zu platzieren. Boxing geschieht, wenn Sie einen Werttyp einem Variablenparameter von Feld- oder Eigenschaftenobjekten zuweisen . Unboxing ist der umgekehrte Prozess. Objekte aus dem Heap werden für den Wert entpackt. Typen im Inneren werden zurück in den Stapel kopiert. Lerne. Boxing geschieht immer dann, wenn Sie keinen Objektwert haben, und Sie es auf einen Werttyp werfen. 8. Wie funktioniert die Garbage, in der man NET? in: in der zweiten Vorlesung dieses Kurses, der im Heap-Speicher im Fundamentals-Abschnitt, haben wir gesehen, was passiert, wenn Referenztypvariablen außerhalb des Geltungsbereichs gehen. Die Variablen, die auf dem Stapel vorhanden sind, werden auf den entsprechenden Objekten auf dem Heap zerstört . R D referenzierte dereferenzierte Objekte bestehen weiterhin und werden nicht sofort zerstört. Ich habe kurz erwähnt, dass ein separater Prozess namens Garbage Collector diese Objekte regelmäßig bereinigt. In diesem Vortrag werden wir uns den Müllsammler genauer ansehen. Was ist der Garbage Collector und wie funktioniert er? Beginnen wir mit einem sehr einfachen Programm. Dieses Programm hat nur eine Meldung mit einem lokalen Variablenobjekt-Array. Das Array wird mit fünf Objekten initialisiert. Diese fünf Objekte befinden sich auch auf dem Heap neben dem Array selbst. Nun, um die Dinge interessanter zu machen, lassen Sie uns Array-Elemente zwei und drei entfernen, indem Sie sitzen. Es Array-Elemente, um die entsprechenden Objekte zu kennen. Nummer zwei und drei gibt es noch auf der Hitze, aber jetzt sind sie de referenziert. Es gibt keine Verweise auf diese Objekte von irgendwo im Mantel. Was passiert, wenn der Müllsammler in den Türnetz-Müllsammler tritt, ist ein Markierungs- und Sweep-Sammler, was bedeutet, dass es zwei verschiedene Stufen gibt, die Müll sammeln eine Markierungsstufe auf einer Sweep-Bühne während der Markierungsphase. Der Garbage Collector markiert alle Lebensobjekte auf der Hitze, so dass in diesem Beispiel das Array selbst sein würde. Auf den Objekten 01 auf vier. Die Objekte zwei und drei werden übersprungen, da es keine Verweise auf diese Objekte von irgendwo im Mantel gibt. Als nächstes kommt die Sweep-Stufe alle Objekte, die nicht haben und in der vorherigen Stufe R D Referenz markiert werden. Und in diesem Stadium werden sie de vom Haufen zugewiesen. In diesem Beispiel haben die Objekte zwei und drei keine Bean markiert, daher sind sie die zugewiesenen auf. Das hinterlässt eine Art Loch auf dem Haufen. Der Hundenetz Müllsammler führt nach dem Sweep einen zusätzlichen Schritt durch, der als kompakt bezeichnet wird. In der kompakten Stufe werden alle Löcher auf dem Heap entfernt, so dass in diesem Beispiel das Objekt vier nach oben verschoben wird, um das Loch zu füllen. Objekt, das hinter dem Markierungs- und Sweep-Garbage Collector bleibt, ist sehr gut darin, jedes einzelne Deal referenzierte Objekte auf dem Heap zu lokalisieren , wenn es entfernt wird, aber es hat auch einen großen Nachteil. Es ist sehr langsam. Während der Markierungsphase muss der Garbage Collector jedes Objekt im Speicher überprüfen, um festzustellen, ob es sich um ein Leben oder um das referenzierte handelt . Wenn sich viele Tausende von Objekten auf dem Heap befinden, wird Ihr Programm tatsächlich für eine Weile einfrieren, da der Garbage Collector jedes einzelne Objekt inspiziert . Der Prozess ist auch sehr ineffizient, da lange lebende Objekte auf dem Heap überprüft und neu überprüft werden , jeden Zyklus durchhält, da sie jederzeit d referenziert werden könnten. So könnte eine sehr lange lebende Objekte Hunderte von Malen überprüft werden, wenn es noch am Leben ist. Die Lösung für dieses Problem wird als Generationen-Garbage Collector bezeichnet. Der Punktnetz-Garbage Collector ist generationell eingeschaltet. Es hat drei Generationen, die Sie als drei separate Heaps visualisieren können. Alle neuen Zuweisungen gehen in den ersten Generationen-Heap namens Generation Zero. Also, wenn wir das Testprogramm mit dem Fünf-Element-Array mit den Elementen zwei und drei begleichen nicht erneut besuchen , dann würde das Speicherlayout so aussehen. Alles ist das gleiche wie zuvor, aber jetzt sind alle Objekte in Generation Null Generationen wohnt man kommt zu unserem Anti. Der erste Sammlungszyklus führt eine Markierung und einen Sweep durch, und alle Objekte, die den Sweep überlebt haben zu Generation eins verschoben. Nach einem Zyklus sieht das Speicherlayout so aus wie dieses Z-Array auf und Objekte 01 und vier haben den Sweep überlebt und befinden sich jetzt in der ersten Generation. Stellen Sie sich nun vor, dass das Programm an diesem Punkt fortgesetzt wird. Tante setzt ein neues Objekt fünf in Array-Elementen auf alle neuen Zuordnungen. Gehen Sie in eine Generation Null, damit die Speicherlayouts so aussehen würden. Wie Sie sehen, ist dies eine interessante Situation. Das Array befindet sich in Generation eins, aber seine Elemente sind in Generationen Null und eins. Das ist vollkommen gültig. Jetzt tritt der Garbage Collector wieder für einen zweiten Zyklus ein. Alle Objekte der Generation 1 bewegen sich zu Generation auf das neue Objekt in Generation Null bewegt sich zur ersten Generation. Wenn das Programm weitergeht und ein neues Objekt sechs in eine seltene Elemente drei setzt, würde es wieder in Generation Null gehen. Wir haben jetzt ein Array in Generation, das sich auf Objekte in Generation 01 bezieht, muss wieder Beweis für die Gewalt, also fragen Sie sich an diesem Punkt vielleicht, warum all das passiert. Warum haben diese drei Generationen? Nun, die Antwort ist sehr einfach. Generationen helfen, die Anzahl von Objekten in Generation Null zu begrenzen. Jeder Sammlungszyklus löscht die Generierung Null von allen Objekten vollständig. Im nächsten Zyklus muss der Garbage Collector also nur neue Objekte untersuchen, die nach dem letzten Zyklus erstellt wurden . Natürlich verschwindet das Problem nicht. Der Garbage Collector hat die Objekte einfach irgendwo anders verschoben. Aber hier sind die Schlüsselgenerationen, die sehr selten gesammelt werden. Der Müllsammler geht davon aus, dass alles, was die Generation erreicht, ein lang lebendiges Objekt sein muss , das nicht sehr oft überprüft werden muss, also löst dies zwei Probleme. Erstens reduziert es die Anzahl von Objekten in Generation Null, so dass der Garbage Collector weniger Arbeit zu tun hat. Zweitens, lange lebende Objekte, die in die Generation überleben, bin ich nicht sehr oft überprüft, was genau das ist, was wir wollen. Der Generationen-Garbage Collector ist eine schöne, leistungsstarke Algorithmen, aber er hat einen wichtigen Nachteil. Es ist ineffizient, da die Verarbeitung großer, lang lebendiger Objekte betrachten, dass große und lange verlassene Objekte in Generation Null zugewiesen werden . Es überlebt den ersten Zyklus, und die Wärme wird verdichtet, was das Objekt möglicherweise im Speicher bewegt. Dann bewegt es sich zur ersten Generation. Es wird auf Bewegungen zur Generation an alle verdichtet. Insgesamt sind dies zwei Teint und zwei Züge, also ignoriert insgesamt vier Speicherkopieoperationen für ein einzelnes Objekt, bevor es in der Generation auf dem Garbage Collector ankommt es für eine Weile. Wenn das Objekt sehr groß ist, können diese vier Kopiervorgänge pro Objekt zu einer Signifikanz-Performance-Gemeinkosten führen. Diese Lösung für dieses Problem besteht also darin, zwei separate Heaps zu haben, einen für kleine Objekte auf einem. Bei großen Objekten sieht das Design so ähnlich wie diese Empörung aus, die zu Hüften sind. Der kleine Objekthaufen, der mit den drei Generationen arbeitet, die wir besprochen haben, bevor Hände die große Kultur halten. Das Besondere an dem großen Objekt-Heap ist, dass er keine Generationen verwendet. In der Tat hat es nur eine einzige Generation, die mit der Generation synchronisiert wird, um das kleine Ziel zu entfernen. Also, wenn die Garbage Collect Prozesse Generierung bis zum kleinen von Ihrem Team waren, läuft es auch durch das gesamte große Ziel. Ein weiterer interessanter Sachverhalt über das große Ziel ist, dass es die während des Sweep-Zyklus nicht verdichtet . Es ist einfach verschmilzt freie Speicherblöcke zusammen, aber es führt keine Komprimierung durch, um die Gesamtmenge des freien Speicherplatzes zu optimieren. Sie fragen sich vielleicht, was bestimmt, ob ein Objekt klein oder groß ist? Nun, die Größenschwelle liegt bei 85 Kilobyte. Alle Objekte mit 85 Kilobyte waren größer geht direkt auf den großen Objekt-Heap. Alle Objekte, die kleiner als diese Grenze sind, gehen in den kleinen Objekt-Heap. Diese zwei separaten Haufen zu haben, löst das Problem von großen, lang lebenden Objekten. Sie müssen nicht mehr viermal kopiert werden, bevor sie in der Generierung in landen, sondern gehen direkt in den großen Objekt-Heap , der nur in der Generation verarbeitet und nie komprimiert wird. Und da hast du es. Der Hund Netze Müllsammler ist eine Generationen-Müllqualität oder verwendet eine Markierung Sweep kompakten Zyklus, und es hat separate Haufen für große Objekte und kleine Objekte. Wenn Sie darüber nachdenken, macht der dot Net Garbage Collector einige sehr spezifische Annahmen über Objekte und Lebenszeiten. Erstens geht es davon aus, dass Objekte entweder kurze Blätter oder lange Lebensdauer sind. Alle Short Lift-Objekte sollten in einem einzigen Sammlungszyklus zugewiesen, verwendet und verworfen werden. Jedes Objekt, das sozusagen durch die Risse rutscht, wird im nächsten Zyklus in Generation eins gefangen, so dass jedes Objekt, das zu Sammelzyklen überlebt, in Generation zu und muss ein langes lebendiges Objekt sein . Außerdem gilt jedes Objekt, das über 85 Kilobyte groß ist, immer als ein lang lebendiges Objekt. Bei der Betrachtung der Sammlungsfrequenz von den verschiedenen Generationen ist klar, dass der Garbage Collector davon ausgeht, dass die überwiegende Mehrheit von Objekten kurzer Dauer sein wird. So kann ich meine Tipps zur Speicheroptimierung in einem einzigen Satz zusammenfassen. Gehe nicht gegen diese Annahmen. Also, was haben wir gelernt? Der Garbage Collector verwendet einen Markierungs-Sweep und einen kompakten Zyklus. Der Garbage Collector verfügt über zwei separate Heaps für große und kleine Objekte. Der große Objekt-Heap auf dem kleinen Ziel. Das kleine Objekt verwendet er drei Generationen. Alle neuen Objekte werden in Generation Null zugeordnet, wenn der Fortschritt in Richtung Generierung dem großen Objekt. Er hat eine einzige Generation, die zusammen mit Generation zu dem kleinen Ziel verarbeitet wird . Außerdem komprimiert der Heap des großen Objekts nicht den Heap. Um den freien Speicher zu optimieren, macht der Garbage Collector zwei wichtige Annahmen über Objektgrößen und Lebensdauer ein 90% Rabatt auf alle Objekte kleiner als 85 Kilobyte müssen kurze Lebensdauer für alle Objekte größer als 85 Kilobyte sein lang gelebt und 9. Tipp Nr: 1: optimiere deinen Code für die Sammlung von garbage: In dieser Vorlesung werden wir uns mehrere Optimierungen anschauen, um den Garbage Collector so schnell wie möglich laufen zu lassen. Aber zuerst, lassen Sie uns zusammenfassen, was wir über den Garbage Collector gelernt haben. In der vorherigen Vorlesung verwendet der DOT-Netz-Garbage Collector einen Markierungs-Sweep im kompakten Zyklus, um D referenzierte Objekte von der Hitze zu bereinigen . Es wird verwendet, Heaps für große und kleine Objekte zu trennen. Der große oder Steg auf diesem kleinen Objekt heapt die kleinen Objekte, die er benutzt. Drei Generationen, alle neuen Objekte werden in Generation Null Hände zugeordnet. Der Fortschritt hin zu Generation Null wird sehr häufig gesammelt. Generationen ein Hände zu viel weniger, so Generationen halfen, die Anzahl von Objekten in Generation Null zu begrenzen . Jeder Sammlungszyklus löscht die Generierung Null vollständig von allen Objekten im nächsten Zyklus , der Garbage Collector muss nur neue Objekte überprüfen, die nach dem letzten Zyklus erstellt wurden . Die erste speicherbasierte Leistungsoptimierung, die wir betrachten werden, besteht darin, einfach die Anzahl von Objekten zu begrenzen, die wir in Generation Null erstellen. Je weniger Objekte erstellt werden, desto weniger Arbeit muss der Garbage Collector erledigen, um den Heap zu bereinigen. Es gibt zwei Strategien, die Sie befolgen können, um die Anzahl von Objekten auf der Hüfte zu begrenzen. Die erste besteht darin, sicherzustellen, dass Ihre Codes keine redundanten Objekte irgendwo auf der zweiten Stelle erstellen , um Ihre Objekte so schnell wie möglich zuzuweisen , zu verwenden und zu verwerfen, so dass sie bereits die vom Garbage Collector im nächsten Zyklus. Wenn Sie zu lange zwischen der Zuweisung und Verwendung beim Verwerfen Ihrer Objekte warten , laufen Sie das Risiko ab, in den Generationen ein oder zwei zu landen. Für kurzlebige Objekte möchten Sie also, dass Ihre Mäntel so eng wie möglich sind. Werfen wir einen Blick auf einige Beispiele. Hier ist ein Codefragment, das 10.000 Mal eine Zeichenfolge aufbaut, indem ein String-Builder mit einem kalten an die appends Methode verwendet wird. Kannst du das Problem mit diesem Mantel sehen? Ich gebe dir 10 Sekunden zum Nachdenken. Hier ist die Lösung. Das Problem ist mit der String-Verkettung Innerhalb der anhängenden Nachricht, Sie werden sich daran erinnern, dass Strings unveränderlich sind, und so erstellt die beiden String-Nachricht für den Zusatz zusätzliche String-Objekte auf dem Heap für jede Schleife, die sie Oration. Die Kälte an der Unterseite vermeidet dieses Problem durch Montage und ruft zweimal nach oben. Der Unterschied ist 40.000 weniger String-Objekte auf dem Heap. Das ist eine große Verbesserung. Also hier ist ein weiteres Beispiel. Sehen Sie, ob Sie das Problem erkennen können. Ich gebe dir wieder 10 Sekunden. Und hier ist die Lösung. Wenn Sie Ganzzahlen in einer Array-Liste speichern, die ganzen Zahlen eingehängt. Die generischen Listen vermeiden dieses Problem, indem sie auf interne Integer-Array anstelle eines Objekt-Arrays verwenden, eine einfache Änderung der Codes, die 20.000 weniger Feld in Ihre Objekte auf der OK führt . Ein weiteres Beispiel. Ein kleines, statisches Objekt wird initialisiert, dann läuft eine Menge anderer Kult zuerst auf. Schließlich wird das Objekt tatsächlich verwendet. Was ist los mit diesem Bild? Ich gebe Ihnen 10 Sekunden, und hier ist die Antwort. Das Objekt ist klein, aber die Lücke zwischen Zuweisung und Verwendung ist sehr groß, so dass es eine große Chance gibt, dass die Objekte in der ersten oder zweiten Generation landen, bevor es verwendet wird. Der Code am unteren Rand vermeidet dieses Problem. Meine erste, die Objekte nicht statisch machen, da sie lokalisiert werden, ist es kurz vor der Verwendung und schließlich setzen die Objektreferenz so, dass sie direkt nach der Verwendung wissen, um an den Garbage Collector zu signalisieren, der ausgeführt wurde , dass die Objekte bereit sind gesammelt. Wenn Sie keine Zuweisungen über Ihre Codes haben möchten, können Sie auch die unteren Codes in der Nachricht umschließen, ebenso wie das Referenzobjekt. Verlassen Sie den Gültigkeitsbereich, wenn Sie die Methoden beenden. Das ist meine Lieblingslösung. Die nächste Optimierung, die Sie durchführen können, besteht darin, die Lebensdauer Ihrer Objekte zu optimieren. Der Garbage Collector geht davon aus, dass das fast alles ist. Kleine Objekte werden kurzlebig sein, und alle großen Objekte werden lange heben, also sollten wir das Gegenteil vermeiden. Kleine, lange Lift, alle tex oder große kurz Motive. Es ist lehrreich, diese Kombinationen in einem Diagramm anzuzeigen. Wenn ich die Objektlebensdauer horizontal auf der Objektgröße vertikal plotten, bekomme ich die folgenden Diagramme. Die unteren linken und oberen rechten Quadranten sind dort, wo Sie sein möchten. Diese Kombinationen von Objekten, Größen und Lebenszeiten stimmen exakt mit den Annahmen aus dem Garbage Collector überein. Die oberen linken und unteren rechten Quadranten stehen im Widerspruch zu den Annahmen aus dem Garbage Collector. Wenn Ihr Code viele Objekte aus diesen Quadranten enthält, arbeiten Sie effektiv gegen die Annahmen aus dem Garbage Collector, dass die Leistung Ihres Mantels wahrscheinlich dadurch leidet. Was können wir tun, um in die richtigen Quadranten zu gelangen? Fangen wir mit Objekten an. Lebenszeit zu Wieder Faktor große, kurze Lift-Objekte müssen wir das Objekt zu erhöhen. Lebenszeit. Dafür gibt es eine sehr einfache Strategie, die Objektpooling genannt wird. Die Idee ist, dass stattdessen oft verwerfen und Objekte und Zuweisung und neue Objekte. Stattdessen verwenden Sie die vorhandenen Objekte wieder. Da die gleichen Objekte immer wieder verwendet werden, wird es effektiv eine lange lebende Objekte. Dies ist eine sehr beliebte Strategie zur Optimierung des Heaps großer Objekte. Schauen wir uns ein Beispiel an. Hier ist ein Fragment von Codes, das eine große Array-Liste zuweist und sie dann zweimal verwendet, wobei bei der Neuzuweisung der Liste zwischen den Verwendungen verworfen wird. Wie würdest du diesen Mantel verbessern? Ich gebe Ihnen 10 Sekunden, um darüber nachzudenken, und hier ist die Lösung. Stattdessen beim Neuzuweisen der Liste das Verwerfen der Liste, stattdessen wischen Sie sie mit einem Aufruf an die klare Nachricht ein, undverwenden Sie dann die Liste für die zweite Methode erneut, rufen Sie den neuen Mantel auf verwenden Sie dann die Liste für die zweite Methode erneut, . Die gleichen Array-Listenobjekte werden immer wieder verwendet. Seine Lebensdauer wird auf die Objekte effektiv erhöht wird langes Leben. Diese Änderung verbessert die Leistung bei verringert die Wahrscheinlichkeit, dass der Heap des großen Objekts fragmentiert wird . Sehen wir uns nun das umgekehrte Problem an. Wir haben eine kleine, lange heben Objekte, die wir brechen müssen oder in eine kurzlebige Objekte. Wie würde das funktionieren? Hier ist ein Beispiel. Dieser Mantel füllt eine Array-Liste mit 10.000 Paarobjekten, Pfund jedes Haar enthält zwei ganze Zahlen. Also, was ist mit diesem Code falsch? Ich gebe dir 10 Sekunden, um darüber nachzudenken. Das Problem ist, dass die Array-Liste ein großes Objekt ist, also geht sie auf den großen Objekt-Heap und wird davon ausgegangen, dass sie lange Lebensdauer hat. Aber die Liste ist mit winzigen Paarobjekten gefüllt, die 10.000 von ihnen entfernt sind. Alle diese Objekte gehen, bis der kleine Objekt-Heap in die Generation Null fällt, da die Array-Liste jedes Aisam einen Verweis beibehält, alle diese Eltern werden nie d referenzieren und schließlich alle in eine Generation übergehen. Die Lösung besteht darin, eine andere beliebte, Wieder Factoring-Strategie zu verwenden , wobei eine Liste mit zwei Ganzzahlen in jedem Listenelement zu haben, brechen wir die Liste der Teile in zwei separate inter generiert, weil eine Ganzzahl ein Wert ist Typ, wird es mit dem Array gespeichert werden. Also jetzt haben wir nur zwei größere Erhöhung im großen Objekt-Heap und absolut nichts in Generation Null. Problem gelöst. Die dritte Optimierung, die Sie durchführen können, ist, Juni die Größe Ihrer Objekte zu finden. Der Garbage Collector geht davon aus, dass fast alle langsamen Objekte von kurzer Dauer sein werden. Alle großen Objekte werden lange Lebensdauer haben. Wenn wir also die Gegensätze in unseren Codes, Schul-Lang-Objekte oder große kurze linke Objekte haben, müssen wir die Größe von diesen Objekten abbrechen, um sie wieder in die richtige Ladung zu bringen. Entsprechung. Beginnen wir mit einem großen kurzen linken Objekt, um die Größe von diesem Objekt zu reduzieren. Es gibt zwei Strategien. Eine teilt das Objekt von Teilen in Unterobjekten auf, jeweils kleiner als 85 Kilobyte oder zwei sind. Reduzieren Sie den Speicherbedarf aus dem Objekt. Hier ist ein Beispiel für die zweite Strategie. Eine Schleife füllt den Puffer mit 32 Tausend, aber es ist, können Sie sehen, was mit seinen Codes falsch ist? Ich gebe dir 10 Sekunden. Und hier ist die Antwort. Die Schleife füllt den Puffer mit Bits, aber der Puffer ist als Array aus ganzen Zahlen definiert. Der Puffer enthält 32.000 Elemente. Andi. Da eine Ganzzahl vier Bytes groß ist, summiert sich dies bis zu 100 28 Tausend Bits. Dies ist über dem Schwellenwert für große Objekte, und daher geht dieser Puffer direkt, bis der große Objekt-Heap auf in der Generation zu der Lösung gesammelt wird , ist es, den Puffer neu zu faktorieren, wie er den Puffer beißt. Jetzt beträgt der Speicherbedarf aus dem Puffer genau 32.000 Bytes, was kleiner ist als die Schwellenwerte für große Objekte. Und so bekommt es Läden auf dem kleinen Ziel in Generation Null, genau wie wir waas. Nun, schauen wir uns das umgekehrte Problem an. Wir haben ein kleines, langlebiges Objekt, das wir Faktor in eine große wieder müssen, lange Aufzüge Objekt. Wie würde das funktionieren? Die Lösung besteht darin, den Speicherbedarf vom Objekt zu vergrößern oder mehrere Objekte zusammenzuführen, um größere Objekte zu erstellen, die auf dem großen Objekt-Heap gehen können. Also hier ist das letzte Beispiel aus dieser Vorlesung, dieser Mantel deklariert eine statische Array-Liste auf. Dann, irgendwo auf halbem Weg, beginnt das Programm, es zu benutzen. Was stimmt mit diesem Code nicht? Ich gebe dir 10 Sekunden. Hier ist die Antwort. Es ist klar, dass das Objekt ein lang lebendiges Objekt sein soll, weil es als statisch deklariert wird . Wenn wir auch wissen, dass die Liste irgendwann mindestens 85 Kilobyte Tage enthält, was? Dann ist es besser, die Liste auf diese Größe zu initialisieren. Dies stellt sicher, dass die Liste direkt auf den großen Projekt-Heap geht, denn wenn Sie die Liste nicht initialisieren, erhält sie die Standardkapazität, die aus dem oberen Teil meines Kopfes 16 Kilobyte beträgt. Dann geht die Liste, bis der kleine Objekt-Heap in Generation Null und schließlich zur Generierung nach potenziell verschoben wird , nachdem er vier Speicherkopieoperationen durchlaufen hat, indem die Liste sofort auf die richtige Größe initialisiert wurde, Sie eine Stimme, die Migration von Generation Null zu Generation vollständig. Es mag seltsam erscheinen, dass Sie den Mantel optimieren können, indem Sie Objekte größer machen, aber genau das tun wir hier. Und manchmal funktioniert es wirklich. Was haben wir gelernt, Ihren Code so zu optimieren, dass der Garbage Collector so schnell wie möglich läuft ? Sie müssen diese Strategien zuerst befolgen, begrenzen Sie die Anzahl von Objekten, die Sie erstellen Sekunden zuweist Verwendung auf verwirft kleine Objekte so schnell wie möglich. Dritte Wiederverwendung aller großen Projekte. Sie möchten mit dem Garbage Collector und nicht dagegen arbeiten. Und so müssen Sie sicherstellen, dass alle Objekte in Ihren Codes entweder klein und kurzlebig sind. Vier große und lange Leben. Also, wenn Sie zufällig Objekte haben, die entweder große und kurze Lift oder kleine auf langen Lift sind , Sie möchten vielleicht wieder Faktor sie für große kurz Themen. Sie können entweder die Lebensdauer erhöhen oder die Größe des Objekts und vier kleine, lange Liftobjekte verringern. Sie können entweder das Lebenszeichen verringern oder die Größe vergrößern. All diese Änderungen werden der Leistung Ihres Mantels zugute kommen. 10. Tipp 2: Vermeide die class: in dieser Vorlesung möchte ich über Finalize er sprechen. Ein finaler Izer ist eine Klassenmethode, die automatisch ausgeführt wird, kurz bevor Objekte vom Garbage Collector bereinigt werden. Fertig stellen. ERs werden manchmal auch D-Strukturen genannt, um anzuzeigen, dass sie die Gegensätze von Konstruktoren sind , während ein Konstrukt er initialisiert ist und Objekte auf es für die erste Verwendung vorbereitet, eine D-Struktur tut die entgegengesetzte Tante jedes Verweilen aufräumt. Die Ressource ist so, dass die Objekte verworfen werden können. Finalize er sind sehr nützlich, wenn Sie knappe Ressource in Ihrem Objekt zuweisen. Beispielsweise auf dem Betriebssystemdateihandle gibt es auf dem Betriebssystemdateihandlenur eine begrenzte Anzahl von Dateihandles zur Verfügung. Daher ist es sehr wichtig, das Handle freizugeben, wenn Sie mit dem Objekt fertig sind. Sicher, Sie könnten eine Close-Nachricht implementieren. Aber was, wenn jemand vergisst, es zu nennen? Es ist sicherer, den Aktengriff in einer finalen Isar freizugeben. So ist es garantiert, dass es freigesetzt wird, wenn das Objekt in einem Mantel aufgeräumt wird, der ungefähr so aussehen würde . Sie können die Klassendeklaration oben im Konstruktor sehen. Diese Initialisierung ist das Objekt. Andi, unterhalb des Konstruktors ist eine seltsam aussehende Methode, die mit einem Tilde-Zeichen beginnt. Das ist die letzte Isar auf. Es wird vom Garbage Collector aufgerufen, kurz bevor das Objekt bereinigt wird. In früheren Vorträgen habe ich Ihnen gezeigt, wie der Müllsammler funktioniert. Sie könnten sich an diese leichte Erinnerung erinnern, mit den drei Generationen auf dem Array von Objekten, die sich durch sie bewegen . Werfen wir einen weiteren Blick auf den Sammlungsprozess. wir an, Nehmenwir an, dass jedes Array-Element eine finalisierte hat. Wenn wir also dieses Szenario mit Elementen zwei und drei wiederholen, die auf Wissen gesetzt sind, dann würden die Speicherlayouts so aussehen. Alles ist das gleiche wie zuvor, mit allen Objekten, die sich in Generation Null Generationen befinden. Eins auf sind leer. Der erste Sammlungszyklus markiert beim Sweep auf allen Objekten, die diese Woche überleben, um zu Generation eins zu wechseln . Nach dem ersten Zyklus sieht das Speicherlayout so aus. Das Array auf den Objekten 01 und vier hat diese Süßigkeit überlebt und ist jetzt in Generation eins. Aber hier ist die Veränderung. Objekte zwei und drei werden nicht bereinigt. Sie können nicht sein, weil sie Er's abgeschlossen haben, die immer noch ausgeführt werden müssen, also verschiebt der Garbage Collector auch Objekte zwei und drei zur Generation eins und fügt sie zu einer speziellen Finalisierung que hinzu, nachdem der Sammlungszyklus eine separate Bedrohung, die als Finalisierungs-Threads bezeichnet wird, wird aktiviert. Andi geht zur Arbeit, die Ausführung des Finalize-er ist von allen Objekten in der Finalisierung Que Lassen Sie uns einen Blick auf tatsächlichen Mantel werfen, den ich geschrieben habe. Ich habe ein kleines Programm gemacht, das Objekte mit Finalize er zuweist, so dass wir sehen können ging. So werfen ihre Anrufe einen Blick auf diesen Code. Hier ist die Hauptprogrammnachricht, die Objekte wiederholt in einer Schleife zuweist. Jedes Objekt erhält eine eindeutige Indexnummer, so dass wir sie einzeln verfolgen können. Wenn ich eine Taste drücke, endet die Schleife. Die Anwendung schreibt einen Text auf die Konsole, dass sie auf dem Hauptprogramm beendet wird. Threads endet, und hier ist die Klassendeklaration aus der My Object-Klasse. Es ist eine sehr einfache Klasse mit nur einem einzigen begraben Ihre Felder Onda, ein Konstruktor zum Initialisieren der Ganzzahl. Der Konstruktor hat hier eine zusätzliche Zeile, die Rechte für die Konsole, in der die Garbage Collection-Generierung , die Objekte Resorts. Andi hier ist die Klasse D Struktur oder finalisieren er. Es schläft für 500 Millisekunden und schreibt dann die aktuelle Generation auf Indexnummer in die Konsole. Okay, lassen Sie uns das Programm starten. Los geht's. Alles scheint viele Objekte zu funktionieren, die in Generation Null konstruiert werden, genau wie wir es erwarten würden. Jetzt werde ich eine Taste drücken. Sie können sehen, dass die Finalize ER einmal alle 500 Millisekunden aufgerufen wird. Die Hauptdrohung des Programms ist beendet, aber die Finalisierung Q ist immer noch voll von Objekten, so dass die Finalisierungs-Threads weiter laufen und Objekte nacheinander abschließen, bis der Prozess schließlich endet. Aber sieh dir das zuerst an. Die Indexnummern sind in zufälliger Reihenfolge, so dass wir wissen, dass der Garbage Collector die Finalize er in zufälliger Reihenfolge Sekunden verarbeitet . Alle Objekte werden in Generation 1 und Schwerter fertiggestellt. Nachdem ich eine Taste gedrückt habe, schließt die Anwendung acht Objekte auf und beendet dann, was mit den Millionen von anderen Objekten passiert. Also, jetzt sollten Sie eine Vermutung haben, die er komplizierte Dinge abschließt. Sense sind leicht falsch zu machen. Lasst uns die Beobachtungen durchlaufen. Eins nach dem anderen beginne ich mit dem zufälligen Rand von den Indexnummern. Wir haben also gesehen, dass der Garbage Collector Objekte in einer völlig zufälligen Reihenfolge verarbeitet. Das bedeutet, dass Ihre endgültigen Isar-Codes keine Annahmen darüber treffen können, dass andere Objekte noch am Leben sind. Alle Verweise, die Ihre Objekte enthalten könnten, konnten bereits abgeschlossen sein. Dies führt zu einer sehr wichtigen Regel Beim Schreiben Finalize ER muss eine finale Isar nur seine eigenen Objekte und nie andere Referenzobjekte verarbeiten. Hier ist ein gutes Beispiel. Werfen Sie einen Blick auf diesen Code. Wenn der Stream Writer zuerst fertig gestellt wird, ist alles in Ordnung. Die Daten werden auf dem Datenträger geleert. Dann wird die Datei geschlossen. Aber wenn die Objekte auf die andere Weise abgeschlossen sind, dann wird der Stream-Writer versuchen, Daten in eine geschlossene Datei in der Anwendung zu schreiben wird zerquetschen. Also würde dieser Mantel tatsächlich 50% Rabatt auf die Zeit abstürzen, abhängig von der Bestellung aus der Finalize-er. Aber in Wirklichkeit passiert das nicht. Microsoft sah dieses Problem kommen und entschied sich, keine finalize er in der Stream-Writer-Klasse zu implementieren . Wenn Sie also vergessen, den Stream-Riser zu schließen, wird Ihr Code den Absturz kennen. Aber die Zeichenfolge wird auch nicht auf die Festplatte geschrieben. Sie verlieren Daten Zusammenfassend können Sie daher keine Annahmen über die Reihenfolge treffen, in der Ihre Objekte fertiggestellt werden, und Sie sollten niemals versuchen, ein referenziertes Objekt in Ihrer endgültigen Isar zu verwenden, da es möglicherweise bereits abgeschlossen sein. Alles klar, das zweite, was wir aus dem Code beobachtet haben, ist, dass alle Objekte in Generation eins fertiggestellt sind . Das macht Sinn, wenn man darüber nachdenkt. Der Garbage Collector markiert alle Lebensobjekte in Generation Null. Andi bringt sie zur ersten Generation. Alle D referenzierten Objekte mit Finalize ERs werden ebenfalls zur Verwahrung in Generation eins verschoben und fügen sie zur Finalisierung hinzu que Die Finalisierungs-Threads verarbeitet die Objekte nacheinander ruft jedes finalize er auf, Entfernen der Objekte aus der Warteschlange. Nach jedem Abschluss hat ER ausgeführt. Die Objekte werden schließlich im nächsten Sammlungszyklus bereinigt. Wir haben in einer vorherigen Vorlesung gesehen, dass der Müllsammler für kleine, kurzlebige Objekte mit kurzer Lift optimiert ist . Ich meine auf Objekt, dessen gesamte Lebensdauer in Generation Null passt. Also hier ist ein großer Nachteil von Objekten mit Finalize er. Ihre Lebensdauer wird immer mindestens Generation Null umfassen. Am ersten Tag dauert es mindestens zwei Sammlungszyklen, bis die Objekte schließlich bereinigt werden, wobei die endgültige Isar zwischen den Sammlungen ausgeführt wird. Und wenn sich viele Objekte in der Warteschlange befinden. Es kann sogar bis zur Generierung dauern, bis das Objekt endgültig abgeschlossen wird. Wenn Sie viele kleine, kurze Lift-Objekte mit Finalize ERs haben , gehen Sie tatsächlich gegen die Garbage Collects oder weil Sie ihre Lebensdauer auf Generation 1 verlängern und dadurch die Anzahl von Speicherkopie-Operationen verdoppeln benötigt, um Sweep und kompakt tief zu markieren. Ein weiterer Nachteil von Finalize er ist, dass jedes Objekt, auf das Sie verweisen, auch in der ersten Generation enden wird . Wenn Sie also eine Sammlungsklasse mit der endgültigen Isar implementieren, bedeutet dies, dass die Sammlung plus alle Elemente in der Sammlung in der ersten Generation endet bevor sie gesammelt wird. Dies führt mich zu einer wichtigen Optimierungsregel. Du solltest nie hinzufügen. Finalisieren er Objekte an den Wurzeln, oft Objekt Graph nur zwei Objekte an der Kante. schließlich Schauen wir unsschließlichdie letzte Mantelbeobachtung an. Wir sahen, dass nach dem Drücken der Taste nur acht Objekte fertig gestellt wurden, bevor die Anwendung beendet wurde. Was ist da passiert? Die Antwort ist eigentlich sehr einfach. Wenn ein Prozess beendet wird, gibt die DOT-Netzlaufzeit der Finalisierungsbedrohung maximal vier Sekunden, um alle verbleibenden Objekte in der Finalisierung que nach diesem Termin zu verarbeiten . Der gesamte Prozess endet auf allen verbleibenden Objekten in der Q r D zugeordnet, ohne ihre endgültige Isar auf diese führt mich zum letzten Punkt über Finalize er. Sie sind nicht garantiert aufgerufen werden, wenn der Host-Prozess etwas verbleibendes in der Finalisierung que Nach vier Sekunden wird er verworfen. Okay, also lassen Sie uns die Nachteile von Finalize er zusammenfassen, die wir bisher gesehen haben. Finalize er sind kühlt in einer zufälligen Reihenfolge. Objekte mit Finalize er enden immer in Generation eins auf, manchmal sogar in der Generation, wenn ich verarbeitet endet, einige finalisieren er möglicherweise nicht aufgerufen, und hier ist eine Zusammenfassung aus. Die Themen, die wir in dieser Vorlesung abschließen ERs behandelt haben, sind Klassenmethoden, die Aufrufe sind, wenn Objekte vom Garbage Collector bereinigt werden. Viele kleine, kurzlebige Objekte mit Finalize Er's sind schlecht, weil die finale Isar ihre Lebenszeit in Generation eins erweitert . Sie sollten nur hinzufügen, abschließen er Objekte an der Kante, oft Objektdiagramm. Fertig stellen. Er sollte niemals auf andere Objekte verweisen. Finalize er sollte extrem kurz sein. Hände laufen sehr schnell 11. Tipp 3: verwende das Muster: Der Garbage Collector in Punktnetzen ist insofern sehr hilfreich, als er den Heap ständig nach D-referenzierten Objekten durchsucht und sie in diskreten Sammlungszyklen bereinigt. Programmierer schwächen, ordnen Sie einfach Objekte zu, wenn wir sie brauchen und vergessen Sie sie. Wenn wir fertig sind, wird uns der Garbage Collector folgen, sozusagen ons unser Chaos bereinigen, wenn auf dem Objekt einige Arten von knapper Ressource zuweisen muss , sagen wir, ein natives Datei-Handle oder ein Grafikgerätekontext oder einen Block aus nativem Speicher, Dann ist der perfekte Ort, um dies zu tun, im Konstruktor. In dem Moment, in dem das Objekt einsatzbereit ist, die Ressource auch bereit und verfügbar. Umgekehrt ist der beste Ort, um die Ressource freizugeben, in der letzten Isar. Die endgültigen Isar-Methoden werden automatisch vom Garbage Collector aufgerufen, kurz bevor die Objekte die zugewiesene erhalten. Dadurch wird sichergestellt, dass die Ressource genau zu dem Zeitpunkt freigegeben wird, zu dem der Garbage Collector das Objekt dezuweist. jedoch Dieser Ansatz hatjedochein großes Problem. Die Zeit zwischen dem Bau und der Fertigstellung kann riesig sein. Der Garbage Collector ist langsam bei Versuchen, so wenig wie möglich auszuführen. Wenn genügend Systemspeicher verfügbar ist, wird der Garbage Collector möglicherweise nicht ausgeführt. Wenn genügend Systemspeicher verfügbar ist, wird Das ist alles, ziehen Sie es vor, den Speicher über Zeiträume außerhalb von Tagen oder Wochen langsam auffüllen zu lassen, wobei eine knappe Ressource für Tage reserviert wird. Er ist eine schreckliche Praxis, die alle Skalierbarkeit tötet. Was es schlimmer macht, ist, dass das Objekt wahrscheinlich nicht mehr verwendet wird und bereits das referenzierte geschlagen hat . Wie beheben wir dieses Problem? Diese Lösung besteht darin, das Entsorgungsmuster zu verwenden. Dieses Entwurfsmuster führt eine entsorgte Nachricht zum expliziten Freigeben ein. Ressource ist, wir einen Blick auf etwas Kälte. Hier ist ein kleines Programm, das ich zwei geschrieben hat, zeigt die Vorteile aus dem entsorgten Muster. Beginnen wir mit der Hauptprogrammnachricht hier. Ich habe zwei statische Felddeklarationen an der Spitze, finalisierte Objekte, die die Gesamtanzahl von finalisierten Objekten und Gesamtzeit halten, die die Gesamtlebensdauer abhalten wird. Alle Objekte werden in Millisekunden kombiniert. Dann reserviere ich hier in dieser Schleife 500 Tausende Objekte bei einem Aufruf, um Methoden zu arbeiten, um einige Arten von Arbeit zu simulieren . Dann, wenn die Schleife endet, zeige ich die Gesamtanzahl der abgeschlossenen Objekte auf der durchschnittlichen Anzahl von Millisekunden, die das Objekt am Leben war. Werfen wir einen Blick auf die ohne entsorgte Klasse. Es ist eine sehr einfache Klasse, die eine Stoppuhr in IT's Konstruktor zuweist und startet. Die Arbeitsmeldung ist eine einfache Schleife, die eine Art Berechnung ausführt, um die Arbeit an der letzten Isar hier unten zu simulieren stoppt die Stoppuhr und aktualisiert die beiden statischen Felder im Hauptprogramm. Beachten Sie, dass ich die interlocked Klasse verwende, um die statischen Felder auf bedrohungssichere Weise zu aktualisieren da möglicherweise mehrere Threads versuchen, die Felder gleichzeitig zu aktualisieren. Die interlocked Klasse wird sicherstellen, dass das alles ist. Threads laufen in der Reihenfolge und kein gelöschter Zustand kann eine Heilung. Okay, lassen Sie mich das Programm ausführen. Sie können die Ergebnisse sehen. Hier gehen wir weiter. Hier sind die Ergebnisse. Das Programm schaffte es, 455.000 Objekte abzuschließen, was bedeutet, dass die verbleibenden 45.000 Objekte immer noch auf dem Heap in Generation Null warten, gesammelt zu werden. Die durchschnittliche Lebensdauer oft Objekt ist 736 Millisekunden. Dies ist also die durchschnittliche Ausweichzeit zwischen dem Bau und der Fertigstellung. Also hier ist unser Problem, das wir vorgeben, dass Objekte sind, eine Art von knapper Ressource zuweist. Aber diese Re-Quellen werden für 736 Millisekunden gehalten, bevor sie ums veröffentlicht werden. Zu einem bestimmten Zeitpunkt gibt es 45.000 nicht gesammelte de referenzierte Objekte auf dem Heap, die darauf warten, gesammelt zu werden aber immer noch an ihrer Ressource festhalten. Dies wird schrecklich für Skalierbarkeit sein. Wir können dieses Problem beheben, indem wir das Entsorgungsmuster verwenden. Werfen Sie einen Blick auf diese Klasse, die hier mit Dispose aufgerufen wurde, in der ich das Muster implementiert habe . Sie können sehen, dass der Konstruktor für die do work-Methoden genau gleich ist. Aber sieh dir diese Schnittstelle hier an. Das entsorgte Muster erfordert, dass Sie die I-Einwegschnittstelle implementieren. Der Interfence definiert eine einzelne Dispose-Methode, bei der Sie alle zugewiesenen explizit freigeben sollen . Knappe Ressource ist, dass ich meine Entsorgungsmethoden hier implementiert habe. Meine Nachricht ruft einfach eine andere geschützte virtuelle Dispose-Methoden auf. Hier unten ist die finale Isar, die auch die geschützten virtuellen entsorgten Methoden aufruft. Sie können sehen, dass die endgültige Isar ein falsches Argument für die öffentlichen entsorgten Methoden bereitstellt, liefert eine wahre Argumente. Also innerhalb der geschützten entsorgten Methoden kann ich nicht unterscheiden, ob der Code von der finalen Isar oder von den öffentlich entsorgten Methoden abgekühlt wird . Dies ist sehr nützlich, weil Sie sich aus der Vorlesung über Finalize er erinnern können, dass es viele Dinge gibt , die Sie innerhalb der letzten Isar nicht tun dürfen, wie zum Beispiel den Zugriff auf Verweise auf andere Objekte. Also, wenn dieses boolesche Argument falsch ist, weiß ich, dass der Code auf der letzten Isar-Bedrohung läuft. Ich kann nicht auf andere Objekte zugreifen. Aber wenn das Argument wahr ist, dann wurde diese Methode von Benutzercodes aufgerufen und alles ist in Ordnung. Ich kann tun, was ich will. Es gibt auch einen entsorgten Boolean hier, der sicherstellt, dass der entsorgte Mantel nur einmal ausgeführt wird . Wenn also jemals Grund war, ruft jemand die öffentlich entsorgten Methoden immer wieder auf und über diesen Mantel wird nur einmal ausgeführt. Schließlich möchte ich Ihnen noch einen coolen Trick zeigen. Stellen Sie sich für eine Sekunde vor, dass die entsorgten Methoden Aufrufe auf die Objekte freigegeben wurden. Seine zugewiesene Ressource ist also jetzt gibt es keinen Bedarf für die finale Isar zu laufen. Das ist alles, weil es nichts mehr zu tun gibt. Sicher, die finale Isar würde eigentlich nichts tun, weil die entsorgte Variable bereits auf true gesetzt ist . Aber denken Sie daran, dass eine finale Isar die Lebenszeit von Objekten bis zu Generation eins verlängern wird. Dies wird ohne Grund Druck auf den Müllsammler ausüben. Glücklicherweise gibt es eine sehr coole Lösung. Sie können tatsächlich die finale Isar für jedes Objekt ausschalten. Diese Codezeile deaktiviert hier die finale Isar, wenn die Entsorgungsmethoden fehlerfrei ausgeführt wurden . Nach dem Aufruf entsorgt haben die Objekte effektiv keine finale Isar mehr, der Müllsammler kann beim nächsten Wahlzyklus sicher verworfen werden und das Objekt wird nie in Generation eins befördert. Okay, also werde ich einige Änderungen am Programm vornehmen, um die neuen Objekte zu verwenden, die das entsorgte Muster implementieren . Zuerst zeige ich Ihnen, was passiert, wenn ich einfach den Typ von Objekten, die erstellt werden, ändere. Nichts anderes tun. Lassen Sie mich das Programm laufen. Los geht's. Und hier sind die Ergebnisse so ziemlich wie der vorherige Lauf. 462.000 Objekte werden fertiggestellt und im Durchschnitt Lebensdauer von 639 Millisekunden. Ich habe jetzt diese coole Nachricht, aber ich rufe sie nicht wirklich an. So wird die Ressource immer noch in der finalen Isar auf der durchschnittlichen Ressource freigegeben. Das Leben ist so ziemlich wie zuvor, also lasst uns das beheben. Ich werde die Art und Weise ändern, wie die Objekte innerhalb der Schleife zugewiesen werden. Ich werde die using-Anweisung verwenden, die garantiert, dass die dispose-Methoden Aufrufe am Ende des using-Blocks erhalten. Das sollte es jetzt tun. Bei jeder neuen Pitchbelüftung wird das Objekt erstellt, verwendet und dann sofort entsorgt. Ich halte mich an die Ressource ist für so kurz wie möglich. Ich werde das Programm erneut ausführen. Schauen Sie sich das an und hier sind die Ergebnisse. Das Programm hat nun alle 500.000 Objekte entsorgt, da die Schleife jedes Objekt vor Beginn der nächsten Iteration entfernt. Bei der durchschnittlichen Off-Lebensdauer ist die Ressource auf erstaunliche 0,26 Millisekunden gesunken. Das ist 245.000 Mal weniger als der Code, der das Entsorgungsmuster nicht verwendet hat. Sie können die Ergebnisse so visualisieren. Wenn dieser horizontale Graph die Zeit darstellt, können wir eine Linie ganz links zeichnen, wenn das Objekt auf einer anderen Linie ganz rechts konstruiert wird , wo das Objekt fertig gestellt wird. Aber dieser Punkt hier ist, wenn wir mit dem Objekt fertig sind, um es aus dem Geltungsbereich gehen zu lassen . Es ist wichtig, an diesem Punkt explizit zu entsorgen, denn sonst würden wir auf die Ressource halten, ist für die gesamte Zeit zwischen de Referenzierung auf Finalisierung, die 245.000 Mal länger ist als die Zeit, die wir tatsächlich das Objekt in erster Linie benötigt. Hier ist eine Zusammenfassung von dem, was wir in diesem Vortrag gelernt haben. Das entsorgte Muster bietet eine Nachricht zum expliziten Freigeben knapper Ressource ist, dass die using-Anweisungen automatisch die dispose-Methode am Ende des using-Blocks aufrufen . Das entsorgte Muster reduziert die Dauer, in der die Ressource von Objekten gehalten wird . Das Aufrufen der Entsorgungsmethoden unterdrückt die endgültige Isar, was verhindert, dass sich die Objektlebensdauer bis zur ersten Generation erstreckt 12. Tipp 4: Vermeide das Boxen und Entpacken: Ich werde Ihnen beibringen, wie Sie die Gedächtniszuweisungen in Ihrem Mantel verbessern können. In dieser Vorlesung wir einen Blick auf das Boxen. Unboxen. Sie fragen sich vielleicht, ob das Boxen Tante Unboxing negative Konsequenzen hat, wie zum Beispiel erhöhten Druck auf den Müllsammler. Nun, lass es uns herausfinden. Ich habe ein Programm geschrieben, um den Unterschied in Speicherzuordnungsmustern zwischen Codefragmenten zu messen , die kein Boxen im Vergleich zu Codefragmenten verwenden, die Boxing verwenden. Hier ist die Küste. Ich werde mit Codes beginnen, die kein Boxen verwenden. Hier ist also die Hauptprogrammmethode, mit einer einfachen Schleife, die eine Testmethode eine Million Mal aufruft. Sie können sehen, dass ich den aktuellen Nuke-Index in die Testnachricht übergebe. Hier ist die Testmethoden Deklaration. Die Testnachricht, außer bei befragten Parametern ons, tut absolut nichts. Ich werde eine freigegebene Rechnungen von diesem Programm in Mono entwickeln erstellen, indem Sie das eingebaute Menü verwenden . Los geht's. Okay, jetzt habe ich Kräuterdatei im Bin Slash Release-Ordner von meinen Projekten ausführen. Der nächste Schritt besteht darin, dieses Programm manuell in der Befehlszeile auszuführen. Der fantastische Look Profiler, der in das Modellframework integriert ist. Lassen Sie mich jetzt zur Befehlszeile wechseln, um ein Mono-Programm auszuführen. Alles, was du tun musst, ist das Stöhnen anzurufen. Oh, führen Sie pflanzliche Andi aus, da die ersten Argumente den ausführenden Kräuterdateinamen bereitstellt, den Sie ausführen möchten, um den Profiler zu aktivieren. Alles, was Sie tun müssen, ist diesen Konfigurationsparameter hier hinzuzufügen Der Profilparameter aktiviert den Look Profiler auf der Berichtsoption zeigt an, dass ich keine Protokolldatei auf der Festplatte möchte. Ich möchte einfach den Leistungsbericht direkt in der Befehlszeile anzeigen. Okay, also los geht's. Ich werde über das Programm auf Dies ist die Leistungsberichte bestellen. Gehen wir durch die Abschnitte eins nach dem anderen, erscheinen er Versionsdaten. Ich benutze Version Nullpunkte vier aus dem Profiler, Die Profiling-Instrumentierung eingeführt auf zusätzliche Overheads off 60 Nanosekunden in meinem Mantel , die nicht, dass Hände. Sie können sehen, dass ich das Programm am 4. September 2015 führe. Als nächstes ist der Jit Abschnitt. Der Just-in-Time-Compiler kompiliert acht Methoden in etwa einem Kilobyte aus der nativen Maschinensprache , und hier werden die Dinge interessant. Der Müllsammler hat nicht genau geheizt. Es hat 21 Objekte während der Heapverdichtung verschoben. Hans führt zwei Sammelzyklen in der ersten Generation durch, die durchschnittlich 1146 Mikrosekunden dauerte. Sie fragen sich vielleicht, warum es keine Generation Zero Sammlungen gibt. Nun, es liegt daran, dass mein Programm überhaupt nichts auf dem Heap zuweist. Alle Objekte im Speicher stammen aus der Punktnetzlaufzeit selbst und wurden erstellt, bevor mein Programm überhaupt gestartet wurde. Deshalb sind sie alle in Generation eins gelandet. Die Zuordnungsübersicht zeigt eine Liste aller Objekte, die während der Ausführung des Programms zugewiesen wurden . Die Liste ist voll von Housekeeping Objekte, aber Sie können sehen, um inter jours hier unten. Einer dieser beiden ist unsere Schleifenvariable. Insgesamtwurden 33 Objekte zugewiesen, wurden 33 Objekte zugewiesen, die 3496 Bisse vom Wärmespeicher belegen. Okay, nein, ich werde eine Änderung am Programm vornehmen, ihre Zellen überprüfen. Ich werde den Typ von den Argumenten von den Testmethoden in Objekte ändern. Also, jetzt, während jeder Schleife, es oration. Die ganze Zahl muss auf dem Heap boxed werden, also ist das der Objekttyp. Argumente können darauf verweisen. Mal sehen, welche Auswirkungen diese Codeänderung auf die Speicherzuordnungsstatistik haben wird . Ich baue das Programm erneut auf, um die ausführende Kräuterdatei auf der Festplatte zu aktualisieren. Dann schalte ich bei einem Lauf zur Befehlszeile um. Das Programm mit Look Profiling wieder. Los geht's. Hier sind die Ergebnisse. Sieh dir die Unterschiede an. Der Garbage Collector hat jetzt fünf Generation Null-Sammlungen Ons für Generation 1-Sammlungen durchgeführt . Die durchschnittliche Zeit pro Sammlung ist ungefähr gleich, aber das ist mehr als das Doppelte der Anzahl der Sammlungen, die wir zuvor hatten. Aber der größte Unterschied liegt in der Allokationszusammenfassung. Ich habe jetzt 1 Million Pfund zugeteilt einzuführen. Kaufen Sie 24 Megabyte Speicher? Der Heap enthält nun eine Million auf 33 Objekten mit 24 Megabyte Speicher. Dies ist sehr interessant, weil Sie wahrscheinlich wissen, dass eine Ganzzahl nur vier Bytes lang ist , aber eine Block-Ganzzahl auf dem Heap nimmt 24 Bits ein, also verwenden wir sechsmal so viel Speicher wie das Szenario. Ohne Boxen ist der Unterschied ziemlich dramatisch. Ich bin von vier Kilobyte von Zuteilern gegangen. Erhitzen Sie den Speicher auf 24 Megabyte und von der Einführung auf eine Million Ameisen auch, was fünf zusätzliche Generation Zero Sammlungen benötigt, um alles aus der Hüfte zu entfernen . Die erste Regel zur Verbesserung der Speicherzuweisungen in Ihren Codes besteht darin, das Boxen beim Unboxen zu vermeiden , wo immer Sie können, da es die unwillkommene Tendenz hat, den Heap mit vielen und vielen kleinen Objekten absolut zu überfluten . Okay, hier ist eine Zusammenfassung von dem, was wir in diesem Vortrag Boxen gelernt haben. Unboxing ist ein Prozess, bei dem Sie Werttypen für Referenztypen austauschbar in Ihrem Mantel verwenden können. Bei jedem Boxvorgang wird ein neues Objekt auf dem Heap erstellt. Ein Feld Objekte belegt mehr Speicher als der ursprüngliche Werttyp. Boxen kann die Hitze mit vielen kleinen Gegenständen überfluten. Setzen Sie erheblichen Druck auf den Müllsammler. Boxen beim Unboxen sollte möglichst vermieden werden. 13. Tipp 5: Füge nicht viele Strings zusammen.: Das Schöne an diesem Abschnitt ist, dass Sie die Schlussfolgerung direkt aus dem Vorlesungstitel lesen können . Anscheinend sollen Sie angeborene Saiten nicht zusammenbauen. Aber warum nicht? Was ist so schlimm daran, ein paar Saiten miteinander zu verbinden? Nun, lass es uns herausfinden. Ich habe ein kleines Testprogramm geschrieben, um die Auswirkungen zu untersuchen, die String-Verkettung auf Speicherzuordnungsarme, Müllund Collector-Verhalten haben wird Müll . Werfen Sie einen Blick auf das. Ich habe die Hauptprogrammnachricht hier bei allem, was es tut, ist, eine abzuschalten, um Methoden zu testen. Die ersten Methoden heißt String-Tests, und es wird hier oben deklariert. Sie können sehen, dass das alles ist, was ich mache, ist, eine zehntausende Zeichenkette zu bauen, die vollständig aus Hash-Zeichen besteht. Ich beginne mit einer leeren Zeichenfolge und dann füge ich in einer Schleife ein einzelnes Zeichen 10.000 Mal hinzu. Jetzt ist das erste, was ich tun möchte, eine Basislinie Messungen einrichten. Also werde ich den Aufruf zum String-Test in der Hauptnachricht hier auskommentieren. Also jetzt tut das Programm absolut nichts, und wir können beobachten, wie sich der Müllsammler in dieser Situation verhält. Wir können auch bestimmen, wie viele Objekte standardmäßig auf dem Heap vorhanden sind. Ich werde das Projekt mit dem Build-Menü erstellen und dann zur Befehlszeile wechseln, damit ich das Programm mit dem Look Profiler ausführen kann, der in das Mona-Framework integriert ist , das Sie in der vorherigen Vorlesung gesehen haben. Das ist alles, was Sie tun müssen, ist die Execute Herbal mit den stöhnen Oh Befehlen aufzurufen, dann stellt diese Profilargumente bereit, um den Protokollprofiler auf Set it zu aktivieren, um die Leistungsberichte direkt auf der Konsole anzuzeigen . Okay, los geht's. Ich führe das Baseline-Programm aus. Nein, und hier sind die Ergebnisse. Der Garbage Collector führt zwei Generation-1-Sammlungen aus. Es gibt 19 Strings auf dem Heap, die etwas über einem Kilobyte Speicher belegen. Der gesamte Heap-Speicher beträgt 3496 Bytes, die von 33 Objekten zugewiesen werden. Also, jetzt werde ich zurück zu Modell entwickeln eine NCAA, bedeutete den Aufruf zum String-Test, baut das Programm neu auf, wechseln Sie zurück zur Befehlszeile und führen Sie das Programm erneut aus. Mal sehen, welche Auswirkungen die String-Verkettung haben wird. Überprüfe das sonst. Sieh dir das an. Es gibt jetzt drei Generationen, null Sammlungen auf sieben Generation 1 Sammlungen in der Zuordnungszusammenfassung, wir können sehen, dass es 10 Tausende und 20 Strings aus dem Heap gibt, etwa 100 Megabyte Speicher belegen. Es gibt auch eine Nachricht Coole Zusammenfassung Abschnitt hier unten. Wir können sehen, dass wir 10.000 Aufrufe an die String-Dot-com-Cat-Methode gemacht haben, die genau wie erwartet ist. Aber die Muschel Katzen Methoden, genannt andere Methoden kühlt interne Zuweisungen. Str Diese Methode wurde 9999 mal aufgerufen und führte zu fast 15.000 Speicherkopieoperationen . Was ist hier los? Der Grund für dieses Verhalten ist, dass Streams unveränderliche Objekte im Punktnetz sind. Einfach ausgedrückt bedeutet dies, dass String-Daten niemals in irgendeiner Weise verändert werden können. Nun, das klingt vielleicht etwas seltsam, denn wenn das so wäre, wie können Methoden wie Ersatz für Khan-Katzen jemals funktionieren? Nun, die Antwort ist, dass diese Methoden die Zeichenfolge nicht direkt ändern. Stattdessen erstellen sie eine völlig neue Zeichenfolge auf dem Heap mit allen Änderungen. In dieser Zeile lassen wir die Textvariable völlig unverändert. Andi erstellt stattdessen eine Kopie von Text vor den Ersetzungen in dieser Kopie auf gibt dann die Kopie und die Storys in den Ergebnissen zurück. Variabel. Das ist eine goldene Regel. Off-String-Operationen in Punkt-Netz-String-Nachricht. Ändern Sie niemals die ursprüngliche Zeichenfolge. Stattdessen erstellen sie immer eine Kopie. Ändern Sie stattdessen die Kopie, wenn Sie diese Kopie zurückgeben. Infolgedessen. Der Grund für dieses Verhalten ist, dass sich Strings wie Wertetypen verhalten, was bedeutet, dass sie nach Wert zugewiesen und verglichen werden können, was für Entwickler sehr praktisch ist. Sie müssen sich nie Sorgen machen, wenn sich String-Variablen auf die gleiche Zeichenfolge auf dem Heap beziehen . Außerdem ermöglicht es der Punktnetzlaufzeit, einige coole Optimierung für Zeichenfolgenbehandlungscodes durchzuführen, um die Leistung zu verbessern. Also in meinem Testprogramm, hier ist, was passiert. Ich beginne mit einer leeren Zeichenfolge. Wenn ich das erste Zeichen hinzufüge. Die Calm Cat Methoden ändern die String-Variable nicht. Stattdessen es eine Kopie aus dem Stream, fügt das Zeichen zur Kopie hinzu und weist die Kopie der Zeichenfolgenvariablen zurück. In der nächsten Schleife, es Belüftung. Dasselbe passiert wieder. Die einzeilige Zeichenfolge wird kopiert. Ein zweites Zeichen wird der Kopie auf der Kopie hinzugefügt wird, wird der String-Variable nach 10 Tausend Verkettung zugewiesen . Ich werde 9999 d referenzierte Strings auf dem Heap haben, plus eine aktive Zeichenfolge mit dem Endergebnis. Dies ist enorm ineffizient, so stellt sich heraus, dass Strings tatsächlich für einen schnellen Vergleich optimiert sind, und sie sind nicht sehr gut in Buchänderungen, um viele Änderungen an einer einzelnen Zeichenfolge durchzuführen . Es gibt eine viel bessere Klasse, die speziell für diesen Zweck entwickelt wurde. Der String-Builder, ein String-Builder, verhält sich eher wie das, was Sie erwarten würden, dass eine Zeichenfolge ein Zeichenpuffer im Speicher auf den Sie frei rechts können, wenn Sie Tante, ein Zeichen für einen String-Builder. Hinter den Kulissen gibt es kein Kopieren. Stattdessen schreibt das Punktnetz-Framework dieses Zeichen einfach direkt in die Zeichenfolge. Also lassen Sie uns das Programm ändern, um stattdessen einen String-Builder zu verwenden und sehen, wie sich dies auf das Speicherzuweisungsmuster auswirkt . Hier ist wieder mein Programm. Es gibt eine andere Testmethoden namens String Builder-Test. Sie können sehen, es ist im Grunde der gleiche Mantel, aber es verwendet stattdessen einen String-Builder, um die 10.000 Zeichen zu erhöhen. Ich werde die Hauptprogrammmethoden ändern, um stattdessen die String-Builder-Testmethoden zu kühlen. Okay, also jetzt wieder, die vertraute Routine aus dem Erstellen des Programms, Umschalten auf die Befehlszeile beim Ausführen des Programms mit Log-Profiling aktiviert. Los geht's. Und hier sind die Ergebnisse. Der Garbage Collector ist zurück zu nur zwei Kollektionen der Generation eins, die mit unseren Baseline-Messungen identisch ist. So setzt der String Builder Coats keinen zusätzlichen Druck. Nun, der Müllsammler überhaupt. Fantastische Tante, Hier ist die Zuteilung Zusammenfassung. Ich habe jetzt 31 Saiten, die etwas über 64 Kilobyte einnehmen. Das ist eine massive Verbesserung. Ich ging von 100 Megabyte auf 64 Kilobyte, was eine 99,9% Verbesserung der Speicher-Footprints ist. Aber halten Sie sich fest. Meine Zeichenfolge enthält schließlich 10 Tausende Zeichen direkt auf einem Unicode-Zeichen in .net nimmt zwei Bisse ein. Warum habe ich 64 Kilobyte von 31 Strings zugewiesen? Sollte meine Zeichenfolge nicht nur 20.000 Bytes sein? Und die Antwort ist, dass der String-Builder anfänglich nur eine winzige Menge von Zeichenspeicher zur Verfügung hat . Wenn der Puffer voll ist, weist der Stream-Builder einen neuen Puffer aus der doppelten Größe zu, wenn alle Daten in diesen neuen Puffer kopiert werden. Für 10.000 Zeichen durchlaufen wir schrittweise Puffergrößen von eins bis vier acht auf 16 Kilobyte, bis wir schließlich 32 Kilobyte erreichen, was für 10.000 Unicode-Zeichen ausreicht. Die einige von 1248 16 und 32 sind 63 Kilobyte. Genau das, was wir jetzt auf dem Heap sehen Um sicherzustellen, dass ich nur die genaue erforderliche Menge an Speicher reserviere , muss ich den String-Builder auf die richtige Größe initialisieren. Wenn der Zeichenpuffer sofort mit 10.000 Zeichen beginnt, muss er sich nicht verdoppeln, während die Schleife läuft. Dies beschleunigt mein Programm und reduziert den Speicherbedarf, um die Änderung zu implementieren . Alles, was ich tun muss, ist Tante ein Konstruktorargument hier, das die Anfangskapazität aus dem String-Builder in Zeichen angibt . Ich werde es auf genau 10 tausend Zeichen initialisieren, also passt alles gut, aber wir haben einen Programmwechsel zur Befehlszeile aufgebaut, um das Programm mit Lok Profiling auszuführen . Hier gehen wir weiter. Hier sind die Ergebnisse 22 Saiten, die etwas über 20 Kilobyte einnehmen. Genau wie wir es erwarten würden, habe ich gerade weitere 70% Rabatt auf unnötige Speicherzuweisungen entfernt. Hier ist eine Zusammenfassung von dem, was wir in dieser Vorlesung String Nachricht gelernt haben. Ändern Sie niemals die ursprüngliche Zeichenfolge. Stattdessen erstellen sie eine Kopie. Ändern Sie die Kopie Tante Rückkehr Das ist Kaffeezeichenfolgen sind für schnelle Vergleiche optimiert. Die String-Builder-Klasse ist für schnelle Modifikationen optimiert. Verwenden Sie immer String-Builder, wenn Sie Strings in Mission Critical Code ändern In meinem Testprogramm Testprogramm reduzierte der Wechsel von Strings zu String-Buildern den Speicherbedarf um 99,9%. 14. Tipp Nr: 6: Verwenden von Strukturen anstelle von Kursen: in diesem Vortrag werde ich einen genaueren Blick auf Struck's Was genau sind geschlagen und wie unterscheiden sie sich von Klassen? Viele Menschen werden verwirrt, wenn sie den Unterschied zwischen Strucks und Klassen erklären müssen. Lasst uns eins nach dem anderen durchgehen. Wir deckten den Unterschied zwischen Werttypen auf den Referenztypen im grundlegenden Abschnitt Cup-Wert wieder. Typen werden entweder auf dem Stapel oder auf dem Heap in Zeile gespeichert, während Referenztypen immer auf ein Objekt verweisen, das an anderer Stelle im Web gespeichert ist. Wir werden später in dieser Vorlesung auf diesen Unterschied zurückkommen, wo Sie sehen werden, dass er einen dramatischen Einfluss auf den Speicherbedarf auf das Garbage Collector-Verhalten hat . Da Striche Werttypen sind, werden sie nach Wert zugewiesen und verglichen. Wenn Sie Strucks zuweisen, erstellt die Punktnetzlaufzeit eine völlig neue Schlagzeilen. Kopien fühlen sich alles vorbei. Vergleichen Sie dies mit einer Klasse, in der die Referenz seine Kopien über Andi Sie mit zwei Verweisen auf das gleiche Objekt auf dem Heeb enden . Dies kann nie mit Strucks passieren, da Striche Werttypen sind, die sie nicht von einer Basisklasse erben können. Sie können Schnittstellen implementieren, aber Sie können keine Implementierung erben. Eine weitere etwas seltsame Einschränkung ist, dass interne Felder keine Initialisierung von Er's haben können. Alle Felder werden automatisch auf ihren Standardwert initialisiert, und Sie können ihre Werte nur im Konstruktor überschreiben. Und wenn wir von Konstruktoren sprechen, kann ein Struck keinen Parameter weniger Konstruktor haben. Sie müssen mindestens ein Argument deklarieren. Der Grund für diese scheinbar seltsame Einschränkung ist, dass die Laufzeit initialisiert Anweisungen durch Nullen ihres Speichers. Im Gegensatz dazu werden Klassen durch den Aufruf ihres Standardkonstruktors initialisiert. Dies macht Strucks viel schneller initialisiert, und dann können Klassen und schließlich Strucks er nicht abgeschlossen haben. Das ist ziemlich offensichtlich, wenn man bedenkt, dass geschlagen unser Wert ist. Typen auf dem Garbage Collector, für die Ausführung von Finalize Er verantwortlich ist, ignorieren immer Werttypen. Es kann dies sicher tun, da Werttypen nicht explizit bereinigt werden müssen. Denk darüber nach. Wenn sich ein Werttyp auf dem Stapel befindet, wird er bereinigt, wenn die Kälte von der Methode zurückkehrt und der entsprechende Stack-Frame verworfen wird, und wenn sich ein Werttyp auf dem Heap befindet, wird er bereinigt, wenn es enthält Typ wird vom Garbage Collector gesammelt. Dies ist eine wichtige Sache, sich daran zu erinnern, dass der Garbage Collector Onley Referenztypen markiert und Sweeps markiert. Es werden immer die Werttypen ignoriert, unabhängig davon, wie sie verwendet werden. Okay, das war also eine kurze Einführung in den Unterschied zwischen Strucks und Klassen. Sehen wir uns nun an, wie sie sich in einem eigentlichen Testprogramm verhalten. Werfen Sie einen Blick auf den folgenden Code. Ich habe Vernunft. Ein kurzes Testprogramm, das eine Million Punktobjekte zuweist, speichert sie in einer generischen Liste. Meine Punktobjekte sind einfache Container für ein X auf einem Y-Wert. Diese Arten von Klassen werden häufig in Programmen angezeigt, die Pixel, Kartenkoordinaten oder mathematische Vektoren verfolgen müssen . Also meine Hauptprogrammnachricht hier unten kühlt eine einzelne Testmethode namens Testklasse. Die Testklassenmethoden von hier lokalisiert eine Million Punkte Klassen in einer Schleife. Initialisieren ist sie mit dem Schleifenzähler. Andi fügt sie zu dieser generischen Liste hinzu. Dies ist die Klassendeklaration für die Punktklasse. Sie können sehen, dass es nur zu öffentlichen Feldern für die X- und Y-Koordinaten sehr unkompliziert . Onda, ein Konstruktor, um die Punkte zu initialisieren. Okay, bevor ich dieses Programm ausführen werde, benötige ich eine Baseline-Messungen, um zu sehen, welche Art von Objekten standardmäßig auf dem Heap vorhanden sind . Also werde ich den Aufruf der Testklassenmethoden in der Hauptprogrammnachricht auskommentieren . Jetzt macht das Programm absolut nichts, und wir können dies verwenden, um eine Baseline für unseren Profilbericht zu erstellen. Ich erstelle jetzt einen Release-Build, wechseln Sie dann zur Befehlszeile, damit ich das Programm mit dem Log-Profiler ausführen kann. Los geht's. Und hier sind die Ergebnisse, die wir zu Generation one Sammlungen durch den Garbage Collector haben. Es gibt 21 Objektverschiebungen aufgrund von Heap-Verdichtungszyklen auf dem enthält 3552 Bits von 33 Objekten zugewiesen wurden. Okay, zurück zu den Codes. Ich werde die Kommentare entfernen, damit wir eine Messung für die Implementierung ausführen können, die Klassen verwendet . Mal sehen, wie der Heap nach der Zuweisung einer Million Punktklassen aussieht. Okay, erstellt eine Freigabebeträge wechseln auf die Befehlszeile ons Führen Sie das Programm aus. Los geht's. Und hier ist unsere Antwort. Dieser Code belastet den Garbage Collector ziemlich. Wir haben vier Kollektionen der Generation Null und drei Kollektionen der Generation eins, die mehr als dreimal so viele Sammlungen haben wie die Basismessung. Der Garbage Collector führt 87.224 Objektverschiebungen durch, während der Heap komprimiert wird. Das ist eine Menge von Speicherkopie-Operationen, die sich negativ auf die Leistung dieses Programms auf Schauen Sie sich die Zuordnungszusammenfassung hier mein Array ist hier, eine einzelne Instanz, die AIDS Megabyte außerhalb des Heatspace belegt. Dies ist, was ich erwarten würde, weil auf Objekt-Referenz auf einem 64-Bit-System acht Bytes groß ist , eine Million Objekte mal acht Bytes ist acht Megabyte, und hier sind die Punkt-Instanzen. Ich habe eine Million Punkte, die 24 Megabyte von Haufen Platz besetzen. Also insgesamt hat mein Programm 32 Megabyte aus dem Speicher zugeteilt. Schließlich in den Methoden „Zusammenfassung schwächen“ die Gesamtlaufzeit aus dem Programm angezeigt. Es sind zwei Punkte 37 Sekunden. Nun fragen Sie sich vielleicht, wie eine Million Punkte bis zu 24 Megabyte aufsummieren. Meine Punktklasse enthält nur zwei ganze Zahlen von jeweils vier Bytes, also würden Sie erwarten, dass eine Million Punkte nur AIDS-Megabyte belegen. Was ist hier passiert? Die Antwort ist, dass jedes Objekt 16 Bytes ohne Overhead einführt , der für Haushaltsdaten verwendet wird, so dass jeder Punkt tatsächlich 24 Bytes groß ist, dreimal größer als erwartet. Mal sehen, ob ich dieses Programm effizienter machen kann, indem ich Strucks anstelle von Klassen verwende. Ich werde die Hauptprogrammnachricht ändern, um die Test-getroffenen Methoden aufzurufen. Stattdessen können Sie aus der Deklaration sehen, dass die Methode ziemlich identisch ist, wie Testklasse acht eine Million Punkte zuweist und sie in einer Liste speichert. Der einzige Unterschied ist, dass ich jetzt Punkte anstelle von Punkteklasse verwende. Also, was ist ein Punkt getroffen? Schauen Sie sich die Erklärung hier an. Es wird einfach mit öffentlichen X- und Y-Feldern auf einem Konstruktor angewiesen, um die Felder zu initialisieren . Beobachten Sie, wenn ich schnell zwischen der Klasse in der Struktur umschalte. Siehst du, sie sind ziemlich identisch. Okay, zurück zur Hauptprogrammnachricht. Ich werde die Testnachricht ändern. Rufen Sie stattdessen Strucks zu verwenden, und jetzt erstellt das übliche Ritual eine Rechnungen. Wechseln Sie zu den Kommandozeilenhändern führen Sie das Programm aus. Er hatte andere Ergebnisse. Wir sind zurück zu zwei Kollektionen der Generation eins und 21 Objektbewegungen, die identisch mit der Basislinie ist. So gibt es jetzt keinen Druck auf die Müllsammlungen oder was auch immer. Die Zuordnungsübersicht zeigt das Array mit acht Megabyte Daten und nichts anderes. Jetzt ist der gesamte Heap-Speicher nur acht Megabyte, ein atemberaubendes Vierfache. Verbesserungen bei Speicherfußabdrücken. Wie ist das passiert? Um den Unterschied zu verstehen, müssen wir visualisieren, wie die Daten im Speicher gespeichert werden. Beginnen wir mit dem Punkteklassencode. Eine Liste von einer Million Punkteklassen sieht auf dem Heap so aus. Jedes Element in der Liste ist ein Acht-Byte-Verweis auf eine Punktklasseninstanz an anderer Stelle im Web, und jede Punktklasseninstanz besteht aus 16 Byte außerhalb von Haushaltsdaten. Tante acht beißt tatsächliche Felddaten ab, also 24 Bits insgesamt. Dies alles summiert sich bis zu acht Megabyte für die Liste auf 24 Megabyte für die Punkte-Instanzen oder 32 Megabyte insgesamt. Vergleichen Sie das jetzt mit den Geschlagenen. Implementierung weist, wie Sie wissen, wie Sie wissen,unsere Stores in der Reihe an, so dass sie direkt in den Listenelementen selbst gespeichert werden. Es gibt keine Referenzen in diesem Szenario. Jeder Schlag hat null Overhead und nimmt nur acht Bisse aus seinem Speicher für die beiden ganzen Zahlen. Das gesamte Array ist also immer noch acht Millionen Bytes, aber ohne zusätzliche Objekte auf dem so. Dadurch wird der Speicherbedarf auf acht Megabyte gesenkt, eine vierfache Verbesserung. Weil es jetzt nur ein einzelnes Array auf dem Heap gibt. Der Garbage Collector muss keine zusätzliche Arbeit erledigen. Es wird einfach das Array entfernt, weil die Punkte innerhalb des Arrays gespeichert sind. Dies bereinigt auch alle eine Million Punkte kostenlos. schließlich Schauen wir unsschließlichdie Programmlaufzeit an. Die Gesamtlaufzeit beträgt jetzt nur 322 Millisekunden. Der Grund dafür ist, dass diese Implementierung nicht eine Million Punkte Constructors Store aufrufen muss. Eine Million Heap-Referenzen im Array. Onda bewegt 870.000 Objekte. Zwei. Kompakt, Der Heap, der viel Zeit spart. Okay, also sind Strucks großartig, und wir sollten sie immer benutzen, richtig? Nun, keine Struktur, großartig, aber Sie sollten sie nur in sehr spezifischen Szenarien verwenden. Hier ist eine Liste. Verwenden von Strichen Wenn die Daten Ihr Story stellt einen einzelnen Wert dar. Beispiele sind ein Punkt. Ein Vektor auf Matrix, eine komplexe Zahl, ein Wertteil. Ein neuer Medic Pupille etcetera, verwenden Streben. Wenn Ihre Datengröße sehr klein ist, 24 Bits oder weniger. Andi, du wolltest Tausende oder Millionen von Instanzen erstellen. Verwenden Sie Strucks. Wenn Ihre Daten unveränderlich sind und nach Wert zugewiesen und verglichen werden sollten, verwenden Sie Struck's. Wenn Ihre Daten in allen anderen Szenarien nicht häufig verpackt werden müssen, sind Klassen eine bessere Alternative. Zusammenfassend ist Struck unsere Werttypen. Klassen sind referenzierte Typen. Strucks sind viel schneller als Klassen, da sie keinen Standardkonstruktor haben. Sie können nicht Tante geerbt werden. Sie werden nicht vom Garbage Collector gesammelt. Strucks weist nur Heap-Speicher für ihre internen Felder zu, und sie haben nicht den 16-Bite-Overhead, den Objekte auf der Hitze in bestimmten Szenarien haben . Verwendung von Strucks anstelle von Klassen kann den Speicherbedarf bei der Laufzeit von Ihrem Mantel drastisch reduzieren . Mein Testprogramm mit Struck's fand ich viermal weniger Speicher und lief sieben Mal schneller, dann die Implementierung, die Klassen 15. Tipp Nr: 7: immer pre-allocate Kollektionen: in diesem Vortrag möchte ich mich auf einen interessanten Aspekt aus Sammlungen konzentrieren, der oft übersehen wird. Aber zuerst wir einen Blick zurück auf die frühere String-Verkettung Vorlesung, Sie werden sich erinnern, dass wir uns die String-Verkettung Onda angesehen haben. Wir entdeckten, dass Saiten unveränderlich sind. Eine ausstehende Menge von Zeichen zu einer Zeichenfolge führte zu einem schrecklichen Speicherbedarf. Da die gesamte Zeichenfolge während jeder Verkettung auf den Heap kopiert wurde, haben wir das Problem behoben, indem wir stattdessen einen String-Builder verwenden. Ein String-Builder ist ein Zeichenpuffer im Speicher, mit einer bestimmten Kapazität, die Sie direkt Rechte zum Ersetzen von Strings durch String-Builder haben eine große Verbesserung des Speicherplatzes von meinem Mantel gemacht. Das ist erinnerst du dich, was passiert, wenn ich die String-Builder-Codes zum ersten Mal ausgeführt habe . Meine Zehntausende Zeichenkette belegte 64 Kilobyte aus Haufen Speicherplatz. Der Grund dafür ist, dass der String-Builder anfänglich nur ein winziges Zeichen hat . Speicher verfügbar Andi. Wenn der Puffer nicht genügend Platz hat, wird er einfach zugeteilt. Ein neuer Puffer aus Doppelte Größe auf kopiert alles in den neuen Puffer. konsekutive Größenänderung ist von 1 bis 4, 8 16 und 32 Kilobyte führten zu 63 Kilobyte aus zugewiesenem Wärmespeicher, viel mehr, als ich tatsächlich für den Strom benötigte. Hier ist eine wichtige Regel. Jede Sammlung auf der Liste Innennetze tut das Gleiche. Eine Liste beginnt mit einer bestimmten Standardkapazität auf, wenn es nicht genügend Platz ist, wird es eine neue Liste erstellen, die doppelt so groß ist, die Hände kopieren alles über. Dies wird den Speicherbedarf von unseren Codes aufblasen und die Leistung negativ beeinflussen. Da alle Speicherkopiervorgänge hinter den Kulissen stattfinden, lassen Sie uns eine Reihe von Messungen durchführen, um herauszufinden, wie viel Speichergemeinkosten die Größenänderung der Liste einführen wird . Werfen Sie einen Blick auf diesen Code. Ich erkläre hier oben eine Reihe von Sammlungen. Honore Liste a que Es ist Stuck, eine genetische Liste auf dem Wörterbuch. Mein Hauptprogramm, Messes, ruft eine einzelne Methode auf, die in seinen Sammlungen aufgerufen wird, wodurch jeder Sammlung ein einzelnes Element hinzugefügt wird. Dies ist notwendig, da einige Sammlungen Lazy Loading implementieren. Sie initialisieren ihren internen Speicher nur, wenn Sie das erste Element hinzufügen. Um jede Sammlung zu initialisieren, muss ich jedem von ihnen mindestens ein Element hinzufügen. Jetzt ist der nächste Schritt ein bisschen funky, um herauszufinden, was die Standardkapazität ist. Ich muss innerhalb der Sammlungsklassen auf ihre interne private Implementierung schauen. Es gibt keine Möglichkeit, dass ich das in Kälte erreichen kann, außer durch Reflexion. Aber ein viel einfacherer Weg ist, mein Programm im Debug-Modus auszuführen, einen Haltepunkt zu setzen und dann mit dem Debunker in die Sammlungen zu schauen. Also werde ich hier und aus dem Programm einen Bruchpunkt setzen. Jetzt kann ich das Überwachungsfenster verwenden, um jede Sammlung zu überprüfen. Alles beginnt mit der Array-Liste. Das interne Array wird ein privates Mitglied sein, daher muss ich den nicht öffentlichen Members-Ordner hier erweitern. Es ist auf Object Array genannt Elemente mit einer Länge aus. Vier. Als Nächstes ist der Cue. Das interne Speicher-Array ist wieder ein privates Mitglied namens Array, mit der Länge von vier. Als nächstes ist der Stuck. Das interne Speicher-Array befindet sich auf einem befragten Array namens Array, mit einer Länge von 16. Als nächstes die generische Liste. Das interne Speicher-Array ist, ähm, interviewte Array genannt Elemente mit der Länge oder vier, und schließlich, das Wörterbuch. Das Wörterbuch hat viele interne Arrays, aber ich konzentriere mich auf dieses hier genannte Schlüssel-Slots, die eine Länge von 12 hat. Also, um eine Ray-Liste vier Elemente que vier Elemente stecken zusammenzufassen. 16 Elemente listet vier Elemente auf. Wörterbuch, 12 Artikel. Sie können sehen, dass dies winzige Anfangskapazitäten sind. Wenn Sie mit dem Hinzufügen von Hunderten oder Tausenden von Elementen zu einer Sammlung beginnen, muss die Größe mehrmals geändert werden, um alle Elemente aufzunehmen. Mal sehen, wie groß die Speicher-Footprints werden können. Ich werde mein Programm modifizieren, um die genetischen Listen mit ein paar Hunderttausende Elemente zu füllen . Also hier unten in der Hauptprogrammnachricht werde ich den Aufruf in seinen Sammlungen auskommentieren. Andi. Stattdessen setzen Sie einen Aufruf der Ausfüllliste Methode Checkouts. Die Film-Liste Methoden. Hier verwende ich eine Schleife, um genau 262.145 Ganzzahlen zur Liste hinzuzufügen. Wie viel Speicher wird für die Liste benötigt? Jedes Element ist eine Vier-Bites-Ganzzahl, die ein Werttyp ist, der in jedem Array-Element in Zeile gespeichert wird, also wird der Gesamtspeicherbedarf viermal 262 Tausende sein. 145 ist 1.048.580 Bits oder so ziemlich ein Megabyte aus dem Speicher. Okay, lassen Sie uns die Theorie testen. Ich werde eine Release-Build-Tante erstellen, den Lock-Profiler ausführen, um genau herauszufinden, was der Speicherbedarf sein wird. Los geht's. Ich führe jetzt das Programm. Hände hier auf die Ergebnisse. Das ganzzahlige Array belegt 4.195.168 Bits oder etwas mehr als vier Megabyte. Was hier passiert ist, ist, dass die genetische Liste ihr internes Array immer wieder dimensioniert , bis schließlich alle 262 Tausende Elemente in das Array passen, aber ich habe diese Zahl nicht zufällig ausgewählt. 262.145 mal 14-Byte-Ganzzahl ist genau ein Megabyte plus vier Bits, so dass der Puffer auf zwei Megabyte erweitern muss, um oder Items on zu fühlen, weil er bereits durch alle fortlaufenden Potenzen von zwei, die insgesamt zugewiesene Heap Speicher beträgt vier Megabyte. Dies stellt das absolute Worst-Case-Szenario dar. Alle Elemente außer dem letzten passen in das Array, so dass die Liste verdoppeln muss, um die letzte Nacht aufzunehmen. Ähm, Sie haben einen Speicherbedarf, der viermal größer ist als das, was tatsächlich benötigt wird . Die Lösung für dieses Problem ist super einfach. Alles, was Sie tun müssen, ist die Liste auf die richtige Größe zu initialisieren. Lass mich das jetzt in Kälte machen. Alles, was ich tun muss, ist die Liste zu ändern. Deklaration hier, Andi, fügen Sie den Wert des Markierungselements dem Konstruktor aus der Liste hinzu. Jetzt muss ich einen neuen Release-Build erstellen dann zur Befehlszeile wechseln, um das Programm erneut auszuführen. Und hier sind die Ergebnisse. Der Speicherbedarf ist zurück auf ein Megabyte, eine vierfache Verbesserung. Lassen Sie mich zusammenfassen, was wir in diesem Vortrag gesehen haben. Alle Kollektionen Indoor-Netze werden auf eine winzige Standardkapazität initialisiert und verdoppeln automatisch ihre Größe, wenn sie voll sind. Dieses Verhalten kann zu einem Worst-Case-Speicherbedarf führen, der viermal größer ist als nötig. Um dieses Problem zu vermeiden, müssen Sie die Sammlung auf die richtige Größe initialisieren 16. Tipp Nr8: Verwirkliche dich nicht die #8: früh: in dieser Vorlesung möchte ich mich auf unerwartete bedauerlich konzentrieren, wenn ich Link-Abfragen schreibe, die die Speicher-Footprints von Ihren Codes stark aufblasen können . Link wurde in C Sharp Version vier eingeführt. Andi. Es ist eine sehr schöne, übersichtliche Sprache, ähnlich wie SQL, um komplexe Abfragen auf unzähligen Datenquellen auszuführen. Link verfügt über viele integrierte Funktionen zum Filtern Projizieren von Aggregieren von Daten. Siri ist hier, wie es funktioniert. Hinter den Kulissen Link kann auf alle Daten arbeiten, die die I unzählige Schnittstelle implementiert . Unzählige Daten sind im Grunde eine Sammlung von Elementen, die Sie durch einen Schritt nach dem anderen gehen können. Die I unzählige Infizierungen enthält nur eine einzige Methode namens Gets in Numerator, zum Beispiel, hassen auf im Zähler, um durch die Daten zu gehen. Die in-Operatorobjekte haben auch eine eigene Schnittstelle. Kalte ich im Zähler. Es enthält nur drei Mitglieder. Gehen Sie neben, um zum nächsten Element in der Serie aktuelle zu bewegen, um den aktuellen Gegenstand abzurufen, Tante Reset, um zurück an den Anfang zu springen, der Siri der Ende Zähler implementiert eine Art aus vorwärts nur Fluch alle für das Abrufen der Gegenstände in der Siri eins nach dem anderen . Wenn Sie die für jede Anweisung verwenden, erstellt der Compiler in M Belüfter hinter den Kulissen, um alle Elemente in der Siri eins nach dem anderen zu durchlaufen . Das Schöne an Link ist, dass es Operationen oben stapeln kann, oft im Operator, ohne sie tatsächlich auszuführen. Betrachten Sie den folgenden Code, den dieser Ausdruck vorbereitet. Arrangieren Sie 500 Quoten Zahlen, aber es erzeugt nicht tatsächlich die Zahlen noch. Alles, was es tut, ist im Zähler Andi zu erstellen. Fügen Sie einen Filterausdruck hinzu, wenn Sie den Bereich durchlaufen. Zum Beispiel, indem Sie die für jede Anweisung verwenden. Der N M Belüfter nutzt die Bewegung. Nächste Nachricht. Tante wendet den Filterausdruck an, um die richtigen Werte zurückzugeben. Eine weitere interessante Szene zu erinnern ist, dass der Bereich keine Erinnerung besetzt. Der In-Operator, Onley, verfolgt die aktuelle Nummer. Andi. Es hat einen Filterausdruck für die Berechnung der nächsten Zahl im Bereich, so dass der Gesamtspeicherbedarf vom in-Operator nur die Größe eines einzelnen Eisen ist. Obwohl der Bereich 500 Elemente beschreibt, können Sie Link verwenden, um sehr große Datenmengen zu verarbeiten, während nur kleine Mengen außerhalb des Hüftspeichers verwendet werden, da der Zähler immer nur das aktuelle Element verfolgt. Aber Link kann sich auf unerwartete Weise verhalten. Betrachten Sie das folgende Programm. Ich habe eine Rechtschreibprüfung in C. Sharp geschrieben. Sie können sehen, dass dieses Projekt als schlechte Rechtschreibprüfung bezeichnet wird, weil es großzügig zugeteilt wird . Speicher auf dem Heap auf erzeugt einen riesigen Speicherbedarf. Werfen wir einen Blick. Ich beginne mit dieser Lese-Dateimethode. Hier öffnet es eine Textdatei liest es Zeile für Zeile auf verwendet die yield return Anweisungen jede Zeile als neues Element in einer unzähligen Sequenz off Strings zurückzugeben. In der Tat wird die gesamte Methode in einen riesigen Krater verwandelt. Die Ertragsrückgabeanweisungen gibt zurück. Der aktuelle Wert auf der move next Methode wird die while-Schleife zur nächsten Zeile In der Datei unten ist hier die Hauptprogrammnachricht. Ich lese die Datei alle Wörter, die ein Wörterbuch von etwa 150 Tausend enthält, richtig geschriebene Wörter. Dann lade ich eine andere Datei Aufrufe Geschichte, die die erste Zeile von der vic PD ist. Ein Artikel über das Land vor Spanien. Dann rufe ich die Rechtschreibprüfungsmethoden auf, die hier oben deklariert werden. Rechtschreibprüfung verwendet eine Linkabfrage, um die einzelnen Wörter in der Story durchzugehen. Für jedes Wort generiere ich die Kleinbuchstaben Version aus den Wörtern und projiziere dann eine neue zu apple, bestehend aus den ursprünglichen Wörtern und einem booleschen Wert, angibt, ob die Wörter wieder im Wörterbuch gefunden werden konnten. Diese Codezeile generiert die Sequenz nicht. Es ist einfach, einen sehr komplizierten Zähler zu bauen oder einen Zauber auszuführen. Überprüfen Sie jedes Wort in der Geschichte. Die Sequenz wird letztendlich hier in der für jede Anweisung generiert. Ich trete durch jeden Schüler in den Ergebnissen. Legen Sie die Konsolenfarbe auf grün oder rot fest, je nachdem, ob das Wort im Wörterbuch gefunden wurde oder nicht, und zeigen Sie dann die Wörter auf der Konsole sowie ein nachstehendes Leerzeichen an. Dieses Programm sollte also die gesamte Geschichte anzeigen. Wörter für Wörter Fans hebt alle Rechtschreibfehler in Rot hervor. Lassen Sie uns das Programm ausführen, um zu überprüfen, ob alles funktioniert, genau wie in früheren Vorträgen, Ich werde einen Release Build Andi erzeugen. Dann werde ich zur Konsolentante wechseln. Ich führe das Programm in der Befehlszeile mit dem Log Profiler aus, also können wir uns alle Objekte ansehen, die auf dem Heap zugewiesen werden. Los geht's. Sie können sehen, dass die Leistung von diesem Code nicht großartig ist. Dies ist wie ein Wort Paar Sekunden oder so. Bei diesem Tempo würde es ewig dauern, den gesamten Wikipedia-Artikel zu überprüfen. Also schätze ich, ich habe Glück, dass ich nur den ersten Satz überprüfe. Okay, also lassen Sie uns warten, bis das Programm abgeschlossen ist. Hier sind die Ergebnisse und sie sind nicht gut. Der Garbage Collector führt 2,6 Millionen Objektverschiebungen durch, während er die 24 Generation Null Sammlungen auf 12 Generation eins Sammlungen verdichtet . In der Allokationszusammenfassung können wir sehen, dass ich 2,7 Millionen Strings auf dem Heeb habe, die 128 Megabyte besetzen. Ich habe auch 349 String-Arrays, die insgesamt 78 Megabyte belegen. Zugeteilter Heap-Speicher ist über 200 Megabyte auf der Methode namens Zusammenfassung zeigt, dass die gesamte Programmlaufzeit 38 Punkte fünf Sekunden beträgt. Was passiert hier? Lassen Sie mich zeigen, dass Sie sich hier nicht geirrt haben. Ich werde zurück zu Mona entwickelt wechseln Jetzt schauen Sie sich diese Methode an, um hier aufzulisten. Ich brauche die Nachricht, weil ich überprüfen muss, ob die Wörter in der Story im Wörterbuch angezeigt werden. Ich mache das mit der „contains“ -Methode. Aber um diese Methoden aufzurufen, muss ich zuerst die Wörterbuchwörter in Bewunderung in eine Liste konvertieren , die Aiken suchen. Hier ist das Problem. Die Wörterbuch-Variable selbst ist in M Belüfter aktiviert, der die Wortliste auf Anfrage lädt. Also für jedes Wort in der Geschichte, dieser Code wird neu laden die gesamte Wortliste auf konvertiert in eine Liste, nur um das einzelne Wort zu überprüfen . Ich habe 18 Wörter in meiner Geschichte, so dass ich am Ende das gesamte Wörterbuch 18 Mal im Gedächtnis habe. Es gibt 145.000 Wörter im Wörterbuch, so dass mir 2,7 Millionen Strings übrig bleibt. Besetzt 128 Megabyte. Die Wörterbuchliste verwendet auf internen Rührungsarray für die Speicherung. Das Wörterbuch benötigt etwas mehr als ein Megabyte Speicher, so dass die Liste auf zwei Megabyte Kapazität auf allen vorherigen verworfenen String-Arrays gewachsen ist, addieren sich bis zu zwei weitere Megabyte, so dass jedes Wörterbuch vier Megabyte aus seinem Gedächtnis an. Da habe ich 18 off sie fizz als bis zu 72 Megabyte aus Wärmespeicher. Zusammen summiert sich das bis zu acht, atemberaubend auf Hunderte Megabyte aus Wärmespeicher, eine massive Verschwendung von Speicher auf diesem ist die erbärmliche, über die ich gesprochen habe. Es ist nicht sofort aus dem Wörterbuch wertvoll ersichtlich, dass es tatsächlich im Operator eingeschaltet ist , der das gesamte Wörterbuch auf den Hügel lädt. Die Rechtschreibprüfung kalt sieht vernünftig aus, aber nur, wenn das Wörterbuch tatsächlich vollständig im Speicher eingelöst wird, so dass wir es schnell abfragen können. Und das ist hier nicht der Fall. Wenn Sie komplexe Link-Abfragen schreiben, ist es sehr einfach zu verlieren. Verfolgen Sie die Implementierung von den Enumeratoren, die Sie verwenden. Wie komplex ist die Implementierung vom In-Operator? Wie viele Gegenstände werden verfügbar gemacht? Sie benötigen diese Informationen, bevor Sie entscheiden können, wie Sie es tun können. Offensichtlich implementiert die Sequenz effektiv eine sehr abstrakte, klare Schicht oben auf C scharfen Mantel, und manchmal kann die Implementierung von den zugrunde liegenden unzähligen Daten zurückkommen, um Sie auf unerwartete Weise zu beißen . Die gute Nachricht ist, dass es nicht schwer ist, die Rechtschreibprüfung zu reparieren. Ich habe das in einem anderen Projekt namens Good Spell Checker gemacht. Werfen wir einen Blick. Die gute Rechtschreibprüfung ist der schlechten sehr ähnlich, aber ein bemerkenswerter Unterschied ist, dass ich das Wörterbuch als eine generische Liste von Strings hier oben deklariere . Diese Änderung beseitigt die Notwendigkeit, Methoden aufzulisten. Rufen Sie den Spellchecker-Mantel an. Beachten Sie auch, dass ich die Liste initialisiere. 250.000 Artikel Dadurch wird verhindert, dass sich die Liste beim Hinzufügen von Elementen verdoppelt. Andi entfernt alle diese d referenzierten String-Arrays auf dem Heap. Die Rechtschreibprüfung ist fast genau die gleiche wie das schlechte Fell. Der einzige Unterschied ist, dass ich jetzt die contains Methoden direkt auf der Wörterbuch-Variable aufrufen kann , da es bereits eine genetische Liste ist. Die letzte Änderung ist in der Hauptprogrammnachricht hier unten. Ich initialisiere jetzt das Wörterbuch, indem ich dies für jede Anweisung verwende. Okay, mal sehen, wie diese Version passt. Ich erstelle einen Release-Build, der auf die Befehlszeile wechselt, wenn das Programm mit dem Protokollprofiler ausgeführt wird . Los geht's. Und hier sind die Ergebnisse. Wir sind auf 145.000 Objektbewegungen hinunter. Eine Generation Zero Sammlung auf Generation eins Kollektionen viel besser. Der Heap enthält jetzt 145.000 Zeichenfolgen. Dies ist unser Wörterbuch, das 6,8 Megabyte aus Heap-Speicher belegt. Ich habe auch neun String-Arrays mit 1,2 Megabyte. Das ist genau das, was Sie erwarten würden. Ein String-Array mit jeweils 145.000 Elementen auf acht Byte Heap-Speicherreferenz wird etwas mehr als ein Megabyte aus dem Speicher belegen , genau das, was wir hier sehen. Diese Implementierung wird also nur das Wort Wörterbuch auf dem Heap auf nichts anderes zuweisen. Dadurch wird der Speicherbedarf auf acht Megabyte gesenkt, eine massive 25-fache Verbesserung darstellt. Also, was haben wir gelernt? Link ist ein leistungsfähiges Framework zum Ausführen von Abfragen über unzählige Daten, die zum Auflisten von Methoden in einem Linkaufrufen , die zum Auflisten von Methoden in einem Link Ausdruck kann unerwartet die Speicherfußabdrücke von Ihrem Mantel aufblasen, um dieses Problem zu beheben . Cool to list vor dem Ausführen der Link-Abfrage In meinen Rechtschreibprüfungscodes vor der Initialisierung des Wort-Wörterbuchs führte zu einem 25-mal kleineren Speicherbedarf. 17. Course: Herzlichen Glückwunsch. Sie haben den gesamten Kurs absolviert. Sie sind jetzt ein zertifizierter C-Sharp-Speicheroptimierer. Ich habe Ihnen gezeigt Wie funktioniert Nets Garbage Collection? Wie optimieren Sie Ihre eigenen Codes für die Garbage Collection? Andi habe ich einige einfache Tricks gezeigt, um die Memory-Footprints von Ihrem Mantel drastisch zu verbessern . Wir haben einen detaillierten Blick auf die Garbage Collection genommen, die Annahmen identifiziert, die der Garbage Collector über die Objektgröße auf Lifetime macht, und erforscht, wie Sie Ihren eigenen Code optimieren können, um mit diesen Annahmen zu arbeiten erforscht, wie Sie Ihren eigenen Code optimieren können, um mit diesen Annahmen zu arbeiten. -Optimierung. Wir haben die Auswirkungen von mehreren einfachen Tricks gemessen, bei denen eine winzige Codeänderung zu einer enormen Verbesserung der Speicherbilanz vor der Küste führte . Die Fähigkeiten, die Sie lernen, haben Ihnen eine umfangreiche Toolbox mit Wissen und Ideen, die Sie verwenden können wenn Sie Ihre eigenen Codes schreiben oder in einem Entwicklungsteam zusammenarbeiten, vor allem, wenn Sie an der kritischen Kälte arbeiten, wo niedrige Speicherauslastung und schnelle Leistung ist von entscheidender Bedeutung. Wenn Sie einige interessante Einblicke aus Ihren eigenen entdecken, teilen Sie diese bitte in der Kursdiskussion für ihn, damit wir alle genießen können. Auf Wiedersehen. Ich hoffe, wir treffen uns wieder in einem anderen Kurs