Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
静态值类型字段是否在C#中的堆中装箱? 只是出于好奇——考虑下面的例子: 公共A类 { 公共静态int-Foo; } 公共课程 { 静态void Main() { //以下变量将分配到 //堆栈并将直接容纳42,因为它是一个 //值类型。 int-foo=42; //以下字段位于上(高频) //堆,但它是否因为是值类型而被装箱? A.Foo=42; } }_C#_.net - Fatal编程技术网

静态值类型字段是否在C#中的堆中装箱? 只是出于好奇——考虑下面的例子: 公共A类 { 公共静态int-Foo; } 公共课程 { 静态void Main() { //以下变量将分配到 //堆栈并将直接容纳42,因为它是一个 //值类型。 int-foo=42; //以下字段位于上(高频) //堆,但它是否因为是值类型而被装箱? A.Foo=42; } }

静态值类型字段是否在C#中的堆中装箱? 只是出于好奇——考虑下面的例子: 公共A类 { 公共静态int-Foo; } 公共课程 { 静态void Main() { //以下变量将分配到 //堆栈并将直接容纳42,因为它是一个 //值类型。 int-foo=42; //以下字段位于上(高频) //堆,但它是否因为是值类型而被装箱? A.Foo=42; } },c#,.net,C#,.net,我的问题如下:是否因为Foo字段位于堆上而将其装箱?或者它是在一个特殊的容器对象/内存部分中封装的,就像实例值类型字段是堆上对象的一部分一样 我假设它没有装箱,但我不确定,也找不到任何关于它的文档 感谢您的帮助。由于斯里拉姆和李在问题的评论中给出了答案,但没有给出答案,我将总结调查结果: 否,该值未装箱。值类型可以驻留在堆上,只有当它们像引用类型一样使用时才会被装箱。 您还可以看到,在我的示例的IL代码中不涉及装箱: .method private hidebysig static void

我的问题如下:是否因为
Foo
字段位于堆上而将其装箱?或者它是在一个特殊的容器对象/内存部分中封装的,就像实例值类型字段是堆上对象的一部分一样

我假设它没有装箱,但我不确定,也找不到任何关于它的文档


感谢您的帮助。

由于斯里拉姆和李在问题的评论中给出了答案,但没有给出答案,我将总结调查结果:

否,该值未装箱。值类型可以驻留在堆上,只有当它们像引用类型一样使用时才会被装箱。

您还可以看到,在我的示例的IL代码中不涉及装箱:

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       12 (0xc)
  .maxstack  1
  .locals init ([0] int32 foo)
  IL_0000:  nop
  IL_0001:  ldc.i4.s   42
  IL_0003:  stloc.0
  IL_0004:  ldc.i4.s   42
  IL_0006:  stsfld     int32 StaticValueTypeFieldBoxing.A::Foo
  IL_000b:  ret
} // end of method Program::Main
CLR不限制类的每个字段都需要具有相同的存储类型。只有实例成员最终进入GC堆。静态成员在加载程序堆中分配。或者在线程本地存储中,当字段具有[ThreadStatic]属性时。这当然强制了静态成员由类的对象的每个实例共享的约定

顺便说一句,实现非常简单,抖动分配存储并知道字段的地址。因此,任何加载和存储都直接使用变量的地址。没有额外的指针解引用,非常有效

所以,不,根本不需要装箱,静态int只会占用4个字节


如果您想亲自查看,请使用“调试+窗口+反汇编”窗口。显示机器代码,您将直接使用变量的地址看到它。每次运行程序时,它都会是一个不同的地址,这是一个恶意软件计数器;DR:是的,但不是语义上的,而且只针对非内置值类型

public static class Program
{
    public static long X;

    public static void Main()
    {
        Program.X = 1234567887654321;
    }
}
以下内容基于我自己对CLR应用程序内部工作的逆向工程

所提供的答案并不完全正确,事实上,相当具有误导性

这是一个有趣的例子。视情况而定

内置类型(由VES直接支持)如int、float等原始存储在静态变量的地址

但有趣的是,非内置类型(如System.Decimal、System.DateTime和用户定义的值类型)被装箱

但有趣的是,他们实际上有点。。。双盒装。想象一下:

public struct MyStruct
{
    public int A;
}

public static class Program
{
    public static MyStruct X;

    public static void Main()
    {
        Program.X.A = 1337;
        Program.DoIt();
    }

    public static void DoIt()
    {
        Program.PrintA(Program.X);
        Program.PrintType(Program.X);
    }

    private static void PrintType(object obj)
    {
        Console.WriteLine(obj.GetType().FullName);
    }

    public static void PrintA(MyStruct myStruct)
    {
        Console.WriteLine(myStruct.A);
    }
}
现在,这将如您所期望的那样工作,MyStruct将为PrintType装箱,而不是为PrintA装箱

然而,Program.X实际上并不像在实例变量或局部变量中那样直接包含MyStruct实例。相反,它在堆上包含对它的引用,其中实例作为一个对象存在,具有一个对象头和全部

如前所述,这不适用于内置类型。因此,如果静态变量包含int,则静态变量将占用4个字节。但是,如果您有一个用户定义类型的静态变量,例如,
struct IntWrapper{public int a;}
,那么静态变量在32位进程中将占用4个字节,在64位进程中将占用8个字节来存储IntWrapper结构的装箱版本的地址,其中,32位进程占用8字节,64位进程占用12字节(对象头指针占用4/8字节,int占用4字节),忽略任何可能的填充

然而,在语义上,它的工作方式与您预期的一样。调用PrintA(Program.X)时,程序将复制Program.X指向的对象中的struct部分(对象头后的数据),并将其传递给PrintA

调用PrintType(Program.X)时,它确实会将实例装箱。代码创建了一个带有对象头的新MyStruct对象,然后将a字段从Program.X引用的对象复制到新创建的对象中,然后将该对象传递给PrintType

总之,Program.X包含装箱MyStruct的地址(如果我们将装箱定义为将值类型转换为引用类型),但仍将装箱(或克隆)该对象,就像它是值类型一样,因此语义保持不变,就像它作为值类型直接存储在静态变量中一样

就像我说的,我不知道他们为什么这样做,但他们确实这样做了

我已经包括了上面C代码的JIT反汇编,并对其进行了注释。 注意,我已经找到了反汇编中的所有名称

对调用的注释:对托管方法的所有调用都是通过指针进行的。在第一次调用中,指针指向负责JIT编译方法的代码。JIT编译后,指针将替换为JIT编译代码的地址,因此任何后续调用都很快

Program.Main:
    MOV     EAX, DWORD PTR DS:[<Program.X>]                 ; Move the address stored in static variable Program.X into register EAX.
    MOV     DWORD PTR DS:[EAX + 4], 539h                    ; Set field at offset 4 (Offset 0 is the object header pointer) to 1337.
    CALL    DWORD PTR DS:[<Program.DoIt Ptr>]               ; Call Program.DoIt.
RET                                                         ; Return and exit the program.

Program.DoIt:
    PUSH    EBP                                             ; Function prologue.
    MOV     EBP, ESP                                        ; Function prologue.
    MOV     EAX, DWORD PTR DS:[<Program.X>]                 ; Move the address stored in static variable Program.X into register EAX.
    MOV     ECX, DWORD PTR DS:[EAX + 4]                     ; Copy the struct part (the dword after the object header pointer) into ECX (first argument (this)), essentially an unboxing.
    CALL    DWORD PTR DS:[<Program.PrintA Ptr>]             ; Call Program.PrintA.
    ; Here, the MyStruct stored in the static value is cloned to maintain value semantics (Essentially boxing the already boxed MyStruct instance).
    MOV     ECX, <MyStructObjectHeader>                     ; Boxing for PrintType: Copy the address of the object header for MyStruct into ECX (First argument).
    CALL    <CreateObject>                                  ; Boxing for PrintType: Create a new object (reference type) for MyStruct.
    MOV     ECX, EAX                                        ; Copy the address of the new object into ECX (first argument for Program.PrintType).
    MOV     EAX, DWORD PTR DS:[<Program.X>]                 ; Boxing for PrintType: Move the address stored in static variable Program.X into register EAX.
    MOV     EAX, DWORD PTR DS:[EAX + 4]                     ; Boxing for PrintType: Get value of MyStruct.A from the object stored in Program.X (MyStruct.A is at offset 4, since the object header is at offset 0).
    MOV     DWORD PTR DS:[ECX + 4], EAX                     ; Boxing for PrintType: Store that value in the newly created object (MyStruct.A is at offset 4, since the object header is at offset 0).
    CALL    DWORD PTR DS:[<Program.PrintType Ptr>]          ; Call Program.PrintType.
    POP     EBP                                             ; Function epilogue.
RET                                                         ; Return to caller.

Program.PrintA:
    PUSH    EAX                                             ; Allocate local variable.
    MOV     DWORD PTR SS:[ESP], ECX                         ; Store argument 1 (the MyStruct) in the local variable.
    MOV     ECX, DWORD PTR SS:[ESP]                         ; Copy the MyStruct instance from the local variable into ECX (first argument to WriteLine).
    CALL    <mscorlib.ni.System.Console.WriteLine(object)>  ; Call WriteLine(object) overload.
    POP     ECX                                             ; Deallocate local variable.
RET                                                         ; Return to caller.

Program.PrintType:
    PUSH    EBP                                             ; Function prologue.
    MOV     EBP, ESP                                        ; Function prologue.
    CMP     DWORD PTR DS:[ECX], ECX                         ; Cause an access violation if 'this' is null, so the CLR can throw a null reference exception.
    CALL    <GetType>                                       ; GetType.
    MOV     ECX, EAX                                        ; Copy the returned System.Type object address into ECX (first argument).
    MOV     EAX, DWORD PTR DS:[ECX]                         ; Dereference object header pointer.
    MOV     EAX, DWORD PTR DS:[EAX + 38h]                   ; Retrieve virtual function table.
    CALL    DWORD PTR DS:[EAX + 10h]                        ; Call virtual function at offset 10h (get_FullName method).
    MOV     ECX, EAX                                        ; Copy returned System.String into ECX (first argument).
    CALL    <mscorlib.ni.System.Console.WriteLine(int)>     ; Call WriteLine.
    POP     EBP                                             ; Function epilogue.
RET                                                         ; Return to caller.
汇编至:

Program.Main:
    PUSH    EBP                                                 ; Function prologue.
    MOV     EBP, ESP                                            ; Function prologue.
    MOV     DWORD PTR DS:[DD4408], 3C650DB1                     ; Store low DWORD of 1234567887654321.
    MOV     DWORD PTR DS:[DD440C], 462D5                        ; Store high DWORD of 1234567887654321.
    POP     EBP                                                 ; Function epilogue.
RET                                                             ; Return.
Program.Main:
    PUSH    EBP                                                 ; Function prologue.
    MOV     EBP, ESP                                            ; Function prologue.
    MOV     EAX, DWORD PTR DS:[3BD354C]                         ; Retrieve the address of the MyStruct object stored at the address where Program.X resides.
    MOV     DWORD PTR DS:[EAX + 4], 3C650DB1                    ; Store low DWORD of 1234567887654321 (The long begins at offset 4 since offset 0 is the object header pointer).
    MOV     DWORD PTR DS:[EAX + 8], 462D5                       ; Store high DWORD of 1234567887654321 (High DWORD of course is offset 4 more from the low DWORD).
    POP     EBP                                                 ; Function epilogue.
RET                                                             ; Return.

在本例中,MyStruct包装了一个长的

public static class Program
{
    public static MyStruct X;

    public static void Main()
    {
        Program.X.A = 1234567887654321;
    }
}
汇编至:

Program.Main:
    PUSH    EBP                                                 ; Function prologue.
    MOV     EBP, ESP                                            ; Function prologue.
    MOV     DWORD PTR DS:[DD4408], 3C650DB1                     ; Store low DWORD of 1234567887654321.
    MOV     DWORD PTR DS:[DD440C], 462D5                        ; Store high DWORD of 1234567887654321.
    POP     EBP                                                 ; Function epilogue.
RET                                                             ; Return.
Program.Main:
    PUSH    EBP                                                 ; Function prologue.
    MOV     EBP, ESP                                            ; Function prologue.
    MOV     EAX, DWORD PTR DS:[3BD354C]                         ; Retrieve the address of the MyStruct object stored at the address where Program.X resides.
    MOV     DWORD PTR DS:[EAX + 4], 3C650DB1                    ; Store low DWORD of 1234567887654321 (The long begins at offset 4 since offset 0 is the object header pointer).
    MOV     DWORD PTR DS:[EAX + 8], 462D5                       ; Store high DWORD of 1234567887654321 (High DWORD of course is offset 4 more from the low DWORD).
    POP     EBP                                                 ; Function epilogue.
RET                                                             ; Return.

另请注意:这些struct对象是为类的所有值类型静态变量分配的,第一次调用访问类中任何静态变量的方法时

也许这就是他们这么做的原因。为了节省内存。如果在静态类中有很多结构,但是没有对使用这些结构的类调用任何方法,那么使用的内存就会更少。如果它们内联在静态类中,那么即使您的程序从未访问它们,每个结构也会毫无理由地占用它们在内存中的大小。通过在堆上第一次将它们作为对象分配