C# 中的静态结构存储在哪里?

Posted

技术标签:

【中文标题】C# 中的静态结构存储在哪里?【英文标题】:Where are static structs stored in C#? 【发布时间】:2021-08-17 17:04:15 【问题描述】:

来自this question 我明白了

结构可以分配在栈上或寄存器中,而不是堆上 如果结构是堆上引用类型对象的一部分,则该结构也将在堆上

但是如果结构体不是对象的一部分,而是类的静态成员,那么呢:

public class Program

    public static CustomStructType inst1;
    
    static void Main(string[] args)
    
        //assigning an instance of value type to the field
        inst1 = new CustomStructType();
    


public struct CustomStructType

    //body

堆上不会有Program 的实例。那么结构体将存储在哪里?

这个问题是this deleted question 的改写版本。该用户已被删除,因此问题和答案随之而来。我还是觉得这个想法很有趣,调试结果更有趣,所以我选择在这里重复。

关于潜在的重复:

this question 创建一个类的实例。如前所述,我知道作为对象一部分存储的结构在堆上。我的代码没有创建类的实例。 this question 让它保持打开状态,不管它是否是静态的,答案是“不,如果你在 Main 内部这样做,一般来说,它不会在堆上分配。” this question Jon Skeet 给出了一个很好的答案,他说每个new 都会在堆栈上分配空间。

【问题讨论】:

哪个 dotnet 的哪个版本?你为什么想知道? @HenkHolterman:非常有效的问题。我想已删除问题的 OP 认为它在不同版本中可能会有所不同。我希望我的答案是独立于版本的,它不包括堆栈并注册为有效的存储位置。为什么有人想知道?我会说,出于教育/学习目的和正确理解。我个人在现实生活中从未关心过它,也从未遇到过问题:-) @PeterDuniho:Eric Lippert 提到了它,但既没有证据也没有解释为什么会这样。而且,正如问题中提到的,当前副本的代码创建了一个实例,而这个问题的代码没有创建一个实例。 @EricLippert:您在这 3 个 cmets 中写的内容非常好,可以理解静态变量不存在于堆栈而不存在于寄存器中的原因。这足以证明。埃里克,我认识你,我知道你永远不会回答你不能 100% 确定的问题。 @EricLippert:您在链接答案中写的句子只是那里的一个句子。它与原始问题有直接关系。它只是为了正确性和完整性。这很棒。然而,这个问题是专门针对静态结构的,所以我不仅想在某处有一个正确的句子,而且至少提供一个逻辑解释为什么它必须是这样的。 【参考方案1】:

也许你错过了它:Eric Lippert 在旁注中提到了它:

[...] 和静态变量存储在堆上。

这是在

的上下文中写的

事实上,这是一个实现细节 [...]

以下是 Microsoft 实现的方式:

但是为什么静态变量会存储在堆上呢?

好吧,即使是 Main() 方法也不会永远存在。 Main() 方法可能会结束,并且其他一些线程可能仍在运行。在这种情况下结构应该发生什么?它不一定在堆上,但我希望你看到它不能在堆栈上而不是在寄存器中。该结构必须位于其他线程仍然能够访问它的地方。堆是一个不错的选择。

Main() 终止的代码示例:

using System;
using System.Threading;

public class Program

    public static CustomStructType inst1;

    static void Main(string[] args)
    
        new Thread(AccessStatic).Start();
        //assigning an instance of value type to the field
        inst1 = new CustomStructType();
        Console.WriteLine("Main is gone!");
    

    static void AccessStatic()
    
        Thread.Sleep(1000);
        Console.WriteLine(inst1);
        Console.ReadLine();
    


public struct CustomStructType

    //body


让我们回到您的原始代码。如有疑问,您可以随时使用调试器进行检查。这是 .NET Framework 4.8 (4.8.4341.0) 中发布版本的调试会话。

我正在使用WinDbg Preview 进行调试,这是 Microsoft 提供的免费调试器。不过用起来不方便。我是从"Advanced .NET debugging" by Mario Hewardt一书中了解到的。

为了简单起见,我插入了Console.ReadLine(),因此我不需要逐步完成所有操作并在正确的时间停下来。

加载 .NET 扩展

ntdll!DbgBreakPoint:
77534d10 cc              int     3
0:006> .loadby sos clr

搜索Program的实例(只是为了检查问题的前提是否正确)确实给出了0个对象:

0:007> !dumpheap -type Program
 Address       MT     Size

Statistics:
      MT    Count    TotalSize Class Name
Total 0 objects

搜索班级:

0:006> !name2ee *!Program
Module:      787b1000
Assembly:    mscorlib.dll
--------------------------------------
Module:      01724044
Assembly:    StructOnHeap.exe
Token:       02000002
MethodTable: 01724dcc
EEClass:     01721298            <--- we need this
Name:        Program

获取有关课程的信息:

0:006> !dumpclass 01721298
Class Name:      Program
mdToken:         02000002
File:            C:\...\bin\Release\StructOnHeap.exe
Parent Class:    787b15c8
Module:          01724044
Method Table:    01724dcc
Vtable Slots:    4
Total Method Slots:  5
Class Attributes:    100001  
Transparency:        Critical
NumInstanceFields:   0
NumStaticFields:     1
      MT    Field   Offset                 Type VT     Attr    Value Name
01724d88  4000001        4     CustomStructType  1   static 0431357c inst1
                                                            ^-- now this

检查垃圾收集堆的位置:

0:006> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x03311018
generation 1 starts at 0x0331100c
generation 2 starts at 0x03311000
ephemeral segment allocation context: none
 segment     begin  allocated      size
03310000  03311000  03315ff4  0x4ff4(20468)
Large object heap starts at 0x04311000
 segment     begin  allocated      size
04310000  04311000  04315558  0x4558(17752)             <-- look here
Total Size:              Size: 0x954c (38220) bytes.
------------------------------
GC Heap Size:    Size: 0x954c (38220) bytes.

是的,它位于从 0x04311000 开始的大对象堆上。

顺便说一句:令我惊讶的是,这么小的“对象”(结构)会被分配到大对象堆上。通常,LOH 将仅包含 85000+ 字节的对象。但这是有道理的,因为 LOH 通常不会被垃圾回收,您也不需要对 static 项目进行垃圾回收。

【讨论】:

以上是关于C# 中的静态结构存储在哪里?的主要内容,如果未能解决你的问题,请参考以下文章

C 和 C++ 中的静态变量存储在哪里?

线性表——顺序存储结构之静态链表

接口的默认方法存储在内存中的哪里?

我在哪里定义内部结构静态对象?看来我不能

java中,静态变量存储在哪个区啊,堆还是栈啊,或者说其他地方,谢谢

调试时,数据存储在哪里