c# länge - Wie viele Bytes werden für 3-Punkt-Strukturen auf einem 64-Bit-Prozessor zugewiesen?




meta title (4)

Es gibt eine Frage:

Gegeben:

struct Point {int x; int y;}
var p = new Point[3]

Wie viele Bytes Speicher werden im Stack und im Heapspeicher zugewiesen, wenn wir einen 64-Bit-Prozessor verwenden?

Die richtige Antwort für .Net ist 44 . Kann jemand erklären, wie diese Nummer erschienen ist?

Soweit ich verstehe, belegt p 8 Bytes im Stack für x64 .

Und wir haben zwei Werte von Int32 pro Struktur, also p.Length * sizeof(Point) 3 * 8 = 24 Bytes im Heap für ein Array.

Das wird 32 Bytes sein . Was ist der Rest 12 Bytes in diesem Fall?


Answers

Das meiste ist lediglich ein Implementierungsdetail und könnte sich mit der nächsten Version der CLR ändern.

Führen Sie das folgende Programm als X 86 oder X 64 aus, und Sie können die Größe Ihrer Struktur empirisch ermitteln:

struct Point { int x; int y; }

class Program
{
    const int Size = 100000;

    private static void Main(string[] args)
    {
        object[] array = new object[Size];
        long initialMemory = GC.GetTotalMemory(true);
        for (int i = 0; i < Size; i++)
        {
            array[i] = new Point[3];
        }
        long finalMemory = GC.GetTotalMemory(true);
        GC.KeepAlive(array);

        long total = finalMemory - initialMemory;
        Console.WriteLine("Size of each element: {0:0.000} bytes",
                          ((double)total) / Size);

    }
}

Der Code ist ziemlich einfach, wurde aber schamlos von Jon Skeet gestohlen .

Wenn Sie dies ausführen, erhalten Sie folgende Ergebnisse:

x86: 36 byte
x64: 48 byte

In der aktuellen Implementierung ist die Größe jedes Objekts an die Zeigergröße angepasst, dh jedes Objekt in x86 ist 4 Byte ausgerichtet und unter x64 8 Byte (dies ist absolut möglich - HotSpot in Java zum Beispiel richtet alles auf 8 Byte aus sogar unter x86).

Arrays in C # sind etwas Besonderes mit ihrer Länge: Während sie ein 4 Byte langes Feld haben, enthalten sie unter x64 auch 4 Byte zusätzliches Padding (vm / object.h: 766 enthält den interessanten Teil). Dies wird höchstwahrscheinlich getan, um sicherzustellen, dass der Anfang der tatsächlichen Felder immer 8 Byte unter x64 ausgerichtet ist, was erforderlich ist, um eine gute Leistung beim Zugriff auf Longs / Doubles / Pointer zu erhalten (die Alternative wäre nur das Auffüllen für diese Typen hinzuzufügen und zu spezialisieren) die Längenberechnung - unwahrscheinlich, dass die zusätzliche Komplexität wert ist.

Auf x86 ist der Objektkopf 8 Byte und der Array-Overhead 4 Byte, was 36 Byte ergibt.

Auf x64 ist der Objektkopf 16 Byte und der Array-Overhead 8 Byte. Dies gibt uns 24 + 24 = 48 Byte.

Für jeden, der einen tatsächlichen Beweis anstelle von empirischen Tests über die Header-Größe und -Ausrichtung wünscht, können Sie einfach zur eigentlichen Quelle gehen: Here ist die Objektdefinition des corelclr. Sieh dir den Kommentar an, der bei Zeile 178 beginnt, der besagt:

// The only fields mandated by all objects are
// 
//     * a pointer to the code:MethodTable at offset 0
//     * a poiner to a code:ObjHeader at a negative offset. This is often zero.  It holds information that
//         any addition information that we might need to attach to arbitrary objects. 

Sie können auch den tatsächlichen Code betrachten, um zu sehen, dass diese Zeiger tatsächliche Zeiger und keine DWORDs oder irgendetwas anderes sind.

Der Code zum Ausrichten der Objektgrößen befindet sich ebenfalls in derselben Datei:

#define PTRALIGNCONST (DATA_ALIGNMENT-1)

#ifndef PtrAlign
#define PtrAlign(size) \
    ((size + PTRALIGNCONST) & (~PTRALIGNCONST))
#endif //!PtrAlign

DATA_ALIGNMENT ist definiert als 4 für x86 (vm / i386 / cgencpu.h) und ARM (vm / arm / cgencpu.h) und 8 für x64 (vm / amd64 / cgencpu.h). Der Code selbst ist nichts anderes als ein standardisiertes optimiertes "Runde zum nächsten Vielfachen von DATA_ALIGNMENT " unter der Annahme, dass die Datenausrichtung eine Potenz der 2 Methode ist.


Ihre Antwort von 44 Bytes ist wahrscheinlich eine Verwirrung, die sich auf ein Array von 32-Bit-Architektur bezieht.

In .Net (32 Bit) :

  • Jedes object enthält 4 Bytes für die Synchronisation ( lock (obj) ).
  • Jedes object enthält 4 Bytes seines Typs Token.
  • Jedes array enthält 4 Bytes seiner Länge.

Der Zeiger ist 8 Bytes wie du gesagt hast.

Dies mit den 24 Bytes des Arrays selbst gibt Ihnen 44 Bytes .

Dies ist jedoch das Header-Layout für 32 Bit.

Wie Sie sehen können, ist das Speicherlayout des folgenden Codes:

var p = new Point[3];
p[0] = new Point { x = 1, y = 2 };
p[1] = new Point { x = 3, y = 4 };
p[2] = new Point { x = 5, y = 6 };

var p2 = new Point[3];
p2[0] = new Point { x = 8, y = 8 };
p2[1] = new Point { x = 8, y = 8 };
p2[2] = new Point { x = 8, y = 8 };

Wird sein:

Sie können die Zahlenwerte auch im Speicherlayout sehen.

In 64 Bit nimmt jedes Feld des Headers 8 Bytes, so dass die Headerlänge 24 Bytes beträgt, daher beträgt die Länge des gesamten Arrays 48 Bytes und mit der Variablen, die auf das Array zeigt: 56 Bytes .

64-Bit-Architektur-Speicherlayout:

Anmerkungen:

  • Wenn Ihr Array nicht auf 8 Byte aufgerundet wurde, würde eine Mehrfachausrichtung stattfinden, aber es ist keine Ausrichtung erforderlich. Beispiel (zwei 1-dimensionale int Arrays) :

  • Obwohl das Längenfeld des Headers 8 Byte in 64 Bit ist, ist es größer als die maximale Array-Größe, die .NET erlaubt, daher können nur 4 verwendet werden.

Beachten Sie, dass dies ein Implementierungsdetail ist und sich möglicherweise zwischen Implementierungen / Versionen der CLR ändert.


Apropos x86 Architektur, die Antwort von 44 Bytes ist nicht korrekt, da die Objektreferenzgröße in x86 4 Byte und nicht 8 Byte beträgt, also die Objektlänge von 36 Byte + 4 Byte Verweis auf das Objekt ergibt 40 Byte . Korrigiere mich, wenn ich falsch liege.


Nein, Sie müssen Ihre eigene Klasse oder Struktur erstellen, um dies zu tun (vorzugsweise eine Klasse, wenn Sie möchten, dass sie änderbar ist - veränderbare Strukturen sind schrecklich).

Wenn Sie nicht an Equals / ToString / GetHashCode Implementierungen GetHashCode sind, ist das ziemlich einfach:

public class MyClass {
    public bool Foo { get; set; }
    public bool Bar { get; set; }
}

(Ich würde aus verschiedenen Gründen immer noch Eigenschaften anstelle von Feldern verwenden.)

Persönlich finde ich normalerweise einen unveränderlichen Typ, den ich zwischen Methoden usw. übergeben kann - ich will eine benannte Version des vorhandenen anonymen Typenmerkmals ...





c# .net arrays memory struct