Dieser Artikel ist ein Spiegelartikel der maschinellen Übersetzung, bitte klicken Sie hier, um zum Originalartikel zu springen.

Ansehen: 537|Antwort: 1

[Quelle] [Dreh dich]. Wie berechnet NET/C#, wie viel Speicher eine Instanz belegt?

[Link kopieren]
Veröffentlicht am 27.8.2025, 09:00:22 | | | |
Wir wissen alle, dass CPU und Speicher die beiden wichtigsten Metriken für ein Programm sind – wie viele Menschen haben also wirklich über die Frage nachgedacht: Wie viele Bytes nimmt eine Instanz eines Typs (Werttyp oder Referenztyp) im Speicher ein? Viele von uns können nicht antworten. C# bietet einige Operatoren und APIs zur Berechnung der Größen, aber keine davon löst das Problem, das ich gerade gestellt habe, vollständig. Dieser Artikel bietet eine Methode zur Berechnung der Anzahl der Speicherbytes, die von Instanzen von Werttypen und Referenztypen belegt werden. Der Quellcode wird hier heruntergeladen.

1. Größe des Operators
2. Marshal. SizeOf-Methode
3. Unsafe.SizeOf-Methode >
4. Kann es anhand des Typs des Feldmitglieds berechnet werden?
5. Anordnung von Werttypen und Anwendungstypen
6. LDFLDA-Richtlinie
7. Berechnen Sie die Anzahl der Bytes des Werttyps
8. Zähle die Anzahl der Bytes des Zitationstyps
9. Vollständige Berechnung

1. Größe des Operators

Die Operationsgröße wird verwendet, um die Anzahl der Bytes zu bestimmen, die von einer Instanz eines Typs belegt werden, aber sie kann nur auf unverwaltete Typen angewendet werden. Der sogenannte Unmanaged Typ ist beschränkt auf:

Primitive Typen: Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double und Single)
Dezimaltyp
Aufzählungstyp
Zeigertyp
Strukturen, die nur Datenmitglieder des Typs Unmanaged enthalten
Wie der Name schon sagt, ist ein Unmanaged Typ ein Werttyp, und die entsprechende Instanz kann keine Referenz auf das verwaltete Objekt enthalten. Wenn wir eine generische Methode wie diese definieren, um den Sizeof-Operator aufzurufen, muss der generische Parameter T der Methode eine unmananged Constraint und ein unsafe-Tag hinzufügen.

Nur native und Enum-Typen können den Sizeof-Operator direkt verwenden, der hinzugefügt werden muss, wenn er auf andere Typen (Zeiger und benutzerdefinierte Strukturen) angewendet wird./unsafeZusammenstellungstags und müssen ebenfalls eingefügt werdenunsicherIm Kontext.

Da die folgende Struktur Foobar kein Unmanaged Typ ist, wird das Programm einen Kompilierungsfehler haben.

2. Marshal. SizeOf-Methode

Statische Typen Marshal definiert eine Reihe von APIs, die uns helfen, nicht verwalteten Speicher zuzuweisen und zu kopieren, zwischen verwalteten und unverwalteten Typen zu konvertieren und eine Reihe weiterer Operationen auf unverwaltetem Speicher auszuführen (Marshal bezeichnet in der Computational Science die Umwandlung von Speicherobjekten in das entsprechende Format für Datenspeicherung oder Datenübertragung). Statisch, zu dem die folgende 4-SizeOf-Methode gehört, überlädt die Anzahl der Bytes eines bestimmten Typs oder Objekts.

Die Marshal.SizeOf-Methode hat keine Einschränkung für den angegebenen Typ für den Unmanaged-Typ, erfordert aber dennoch eine AngabeWerttyp。 Wenn das eingehende Objekt ein Objekt ist, muss es auch eine Box für einen Werttyp sein.

Da folgendes Foobar definiert ist als:Art, sodass Aufrufe beider SizeOf-Methoden eine ArgumentException-Ausnahme auslösen und prompten: Typ 'Foobar' kann nicht als unmanaged Struktur marshalled werden; Es kann keine sinnvolle Größe oder Versatz berechnet werden.

Marshal.SizeOf-MethodeGenerika werden nicht unterstützt, hat aber auch Anforderungen an die Anordnung der Struktur, die die Stütze unterstütztSequenziellundExplizitLayout-Modus. Da die unten gezeigte Foobar-Struktur den Auto-Layout-Modus übernimmt (Auto, der aufgrund der strengeren Speicherlayoutanforderungen in nicht verwalteten Umgebungen keine "dynamische Planung" des Speicherlayouts auf Basis von Feldmitgliedern unterstützt), werden Aufrufe der SizeOf-Methode weiterhin dieselbe ArgumentException-Ausnahme wie oben angezeigt.

3. Unsafe.SizeOf-Methode

Static Unsafe bietet mehr Low-Level-Operationen für unverwalteten Speicher, und ähnliche SizeIOf-Methoden sind ebenfalls in diesem Typ definiert. Die Methode hat keine Einschränkungen auf den angegebenen Typ, aber wenn Sie einen Referenztyp angeben, gibt sie dasAnzahl der Zeigerbytes"(IntPtr.Size)。

4. Kann es anhand des Typs des Feldmitglieds berechnet werden?

Wir wissen, dass sowohl Wert- als auch Referenztypen als kontinuierliches Fragment abgebildet werden (oder direkt in einem Register gespeichert werden). Der Zweck eines Typs ist es, das Speicherlayout eines Objekts anzugeben, und Instanzen desselben Typs haben dasselbe Layout und die Anzahl der Bytes ist naturgemäß gleich (für Felder des Referenztyps speichert er nur die referenzierte Adresse in dieser Byte-Sequenz). Da die Bytelänge durch den Typ bestimmt wird, könnten wir, wenn wir den Typ jedes Feldmitglieds bestimmen könnten, doch die Anzahl der Bytes berechnen, die diesem Typ entsprechen? Tatsächlich ist das nicht möglich.

Zum Beispiel wissen wir, dass die Bytes von Byte, Short, Inct und Long 1, 2, 4 und 8 sind, sodass die Anzahl der Bytes für eine Byte-Binärform 2 beträgt, aber bei einer Typkombination von Byte + Short, Byte + Int und Byte + Long sind die entsprechenden Bytes nicht 3, 5 und 9, sondern 3, 8 und 16. Denn das betrifft das Problem der Gedächtnisausrichtung.

5. Anordnung von Werttypen und Referenztypen

Die Anzahl der Bytes, die von Instanzen des Referenztyps und Subtyps eingenommen werden, variiert ebenfalls für genau dasselbe Datenelement. Wie im folgenden Bild gezeigt, ist die Byte-Sequenz der Werttyp-InstanzAlle sind Feldmitglieder, die zur Lagerung verwendet werden。 Für Instanzen von Referenztypen wird auch die Adresse der entsprechenden Typ-Methodentabelle vor der Feldbyte-Sequenz gespeichert. Die Methodentabelle liefert fast alle Metadaten, die den Typ beschreiben, und wir verwenden diese Referenz, um zu bestimmen, zu welchem Typ die Instanz gehört. Ganz vorne gibt es außerdem zusätzliche Bytes, die wir als "Bytes" bezeichnen werdenObjekt-HeaderEr wird nicht nur verwendet, um den gesperrten Zustand des Objekts zu speichern, sondern der Hashwert kann hier auch zwischengespeichert werden. Wenn wir eine Referenztyp-Variable erstellen, wird diese VariableSie zeigt nicht auf das erste Byte Speicher, das von der Instanz belegt ist, sondern auf den Ort, an dem die Adresse der Methodentabelle gespeichert ist



6. LDFLDA-Richtlinie

Wie wir oben eingeführt haben, können der Sizeof-Operator und die SizeOf-Methode, die vom statischen Typ Marshal/Unsafe bereitgestellt werden, die Berechnung der von Instanzen eingenommenen Byte-Länge nicht wirklich lösen. Soweit ich weiß, kann dieses Problem nicht allein im C#-Bereich gelöst werden, aber es wird auf IL-Ebene bereitgestelltLDFLDAAnleitung kann uns helfen, dieses Problem zu lösen. Wie der Name schon sagt, steht Ldflda für Load Field Address, was uns hilft, die Adresse eines Feldes in der Instanz zu erhalten. Da diese IL-Instruktion in C# keine entsprechende API hat, können wir sie nur in folgender Form mit IL Emit verwenden.

Wie im obigen Codeschnipsel gezeigt, haben wir eine GenerateFieldAddressAccessor-Methode im SizeCalculator-Typ, die einen Delegierten vom Typ Func<object?, long[]> basierend auf der Liste der Felder des angegebenen Typs generiert, was uns hilft, die Speicheradresse des angegebenen Objekts und alle seine Felder zurückzugeben. Mit der Adresse des Objekts selbst und der Adresse jedes Feldes können wir natürlich den Offset jedes Feldes erhalten und dann einfach die Anzahl der Speicherbytes berechnen, die die gesamte Instanz belegt.

7. Berechnen Sie die Anzahl der Bytes des Werttyps

Da Wertetypen und Referenztypen im Speicher unterschiedliche Layouts haben, müssen wir auch unterschiedliche Berechnungen verwenden. Da das Byte der Struktur der Inhalt aller Felder im Speicher ist, verwenden wir eine clevere Methode, um es zu berechnen. Angenommen, wir müssen die Anzahl der Bytes einer Struktur vom Typ T festlegen, dann erstellen wir ein ValueTuple<T,T> Tupel, und der Offset seines zweiten Feldes Item2 ist die Anzahl der Bytes der Struktur T. Die spezifische Berechnungsmethode spiegelt sich in der folgenden CalculateValueTypeInstance-Methode wider.

Wie im obigen Code-Snippet gezeigt, nehmen wir an, dass der Strukturtyp, den wir berechnen müssen, T ist, die Methode GetDefaultAsObject aufgerufen, um das default(T)-Objekt in Form einer Spiegelung zu erhalten, und erstellen dann ein ValueTuple<T,T>tuple. Nachdem wir die Methode GenerateFieldAddressAccessor aufgerufen haben, um den Func<object?, long[]> Delegierten zur Berechnung der Instanz und ihrer Feldadressen zu erhalten, rufen wir diesen Delegierten als Argument auf. Für die drei Speicheradressen, die wir erhalten, sind das Code-Tupel und die Adressen der Felder 1 und 2 gleich, wir verwenden die dritte Adresse für Item2 minus die erste Adresse und erhalten das gewünschte Ergebnis.

8. Zähle die Anzahl der Bytes des Zitationstyps

Die Byte-Berechnung für Referenztypen ist komplizierter, wobei diese Idee verwendet wird: Nachdem wir die Adresse der Instanz selbst und jedes Feldes erhalten haben, sortieren wir die Adressen, um den Offset des letzten Feldes zu erhalten. Fügen wir diesen Offset zur Anzahl der Bytes des letzten Feldes hinzu und fügen dann die notwendigen "ersten und letzten Bytes" zu dem gewünschten Ergebnis hinzu, das sich in der folgenden CalculateReferneceTypeInstance-Methode widerspiegelt.

Wie im obigen Codeausschnitt gezeigt, gibt CalculateReferneceTypeInstance die minimale Anzahl von Bytes der Referenztypinstanz zurück, wenn der angegebene Typ keine definierten Felder hat: das Dreifache der Anzahl der Adresszeiger-Bytes. Für x86-Architekturen nimmt ein Anwendungstypobjekt mindestens 12 Bytes ein, einschließlich ObjectHeader (4 Bytes), Methodentabellenzeiger (Bytes) und mindestens 4 Bytes Feldinhalt (diese 4 Bytes sind erforderlich, auch wenn kein Typ ohne Felder definiert ist). Im Fall der x64-Architektur beträgt diese Mindestanzahl an Bytes 24, da der Methodentabellenzeiger und der minimale Feldinhalt 8 Bytes betragen, obwohl der gültige Inhalt des ObjectHeaders nur 4 Bytes einnimmt, aber 4 Bytes Padding vorne hinzugefügt werden.

Die Settlement der Bytes, die vom letzten Feld eingenommen werden, ist ebenfalls sehr einfach: Ist der Typ ein Werttyp, wird die zuvor definierte CalculateValueTypeInstance-Methode zur Berechnung aufgerufen; ist es ein Referenztyp, der im Feld gespeicherte Inhalt ist nur die Speicheradresse des Zielobjekts, sodass die Länge IntPtr.Size ist. Da Referenztyp-Instanzen standardmäßig mit IntPtr.Size im Speicher ausgerichtet sind, wird dies hier ebenfalls gemacht. Vergessen Sie schließlich nicht, dass die Referenz der Referenztyp-Instanz nicht auf das erste Speicherbyte zeigt, sondern auf das Byte, das den Methodentabellenzeiger speichert, sodass Sie die Anzahl der Bytes von ObjecthHeader (IntPtr.Size) addieren müssen.

9. Vollständige Berechnung

Die beiden Methoden, die zur Berechnung der Anzahl der Bytes von Werttyp- und Referenztypinstanzen verwendet werden, werden in der folgenden SizeOf-Methode verwendet. Da der Aufruf der Ldflda-Instruktion eine entsprechende Instanz bereitstellen muss, stellt diese Methode einen Delegierten zur Verfügung, der zusätzlich zum Zieltyp die entsprechende Instanz erhält. Die Parameter, die diesem Delegierten entsprechen, können standardmäßig gesetzt werden, und wir verwenden den Standardwert für den Werttyp. Für Referenztypen versuchen wir außerdem, das Zielobjekt mit dem Standard-Konstruktor zu erstellen. Wenn dieses Delegiertenobjekt nicht bereitgestellt wird und die Zielinstanz nicht erstellt werden kann, wirft die SizeOf-Methode eine Ausnahme. Obwohl wir die Zielinstanz bereitstellen müssen, steht das berechnete Ergebnis nur im Zusammenhang mit dem Typ, daher cachen wir das berechnete Ergebnis. Zur einfacheren Anrufbarkeit bieten wir auch eine weitere allgemeine <T>SizeOf-Methode an.

Im untenstehenden Codeausschnitt verwenden wir es, um die Anzahl der Bytes zweier Strukturen und Typen mit derselben Felddefinition auszugeben. Im nächsten Artikel erhalten wir den vollständigen Binärinhalt der Instanz im Speicher basierend auf der berechneten Anzahl der Bytes, also bleiben Sie dran.

Originallink:Der Hyperlink-Login ist sichtbar.




Vorhergehend:Das Frontend-Framework lernt das Open-Source-Projekt Component-Party
Nächster:MinIO-Speicher (iii) Kopieren-hochladen (migrieren) lokale Dateien in den Minio-Bucket
 Vermieter| Veröffentlicht am 27.8.2025, 09:33:22 |
Die C#-Sammlung fügt 10.000 Datenstücke ein, die den Speicher belegen




Code:


Verzichtserklärung:
Alle von Code Farmer Network veröffentlichten Software, Programmiermaterialien oder Artikel dienen ausschließlich Lern- und Forschungszwecken; Die oben genannten Inhalte dürfen nicht für kommerzielle oder illegale Zwecke verwendet werden, andernfalls tragen die Nutzer alle Konsequenzen. Die Informationen auf dieser Seite stammen aus dem Internet, und Urheberrechtsstreitigkeiten haben nichts mit dieser Seite zu tun. Sie müssen die oben genannten Inhalte innerhalb von 24 Stunden nach dem Download vollständig von Ihrem Computer löschen. Wenn Ihnen das Programm gefällt, unterstützen Sie bitte echte Software, kaufen Sie die Registrierung und erhalten Sie bessere echte Dienstleistungen. Falls es eine Verletzung gibt, kontaktieren Sie uns bitte per E-Mail.

Mail To:help@itsvse.com