C# 中的静态结构存储在哪里?
Where are static structs stored in C#?
从this question我了解到
- 结构可以分配在栈上或寄存器中而不是堆上
- 如果结构是堆上引用类型对象的一部分,则该结构也将在堆上
但是一个结构不是对象的一部分,而是 class 的静态成员,像这样:
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 创建一个 class 的实例。如前所述,我知道作为对象的一部分存储的结构在堆上。我的代码没有创建 class.
的实例
- this question 无论它是否是静态的,它都保持打开状态,答案是“不,如果你在 Main 内部这样做,一般来说,它不会在堆上分配。”
- this question 有 Jon Skeet 的一个很好的答案,它说每个
new
在堆栈上分配 space。
也许您错过了:Eric Lippert 在旁注中提到了它:
[...] and static variables are stored on the heap.
这是在
的上下文中写的
The truth is that this is an implementation detail [...]
Here's how the Microsoft implementation does it:
但是为什么静态变量存储在堆上呢?
好吧,即使是 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, which is a free debugger provided by Microsoft. It's not convenient to use, though. I learned about it from the book "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
搜索 class:
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
获取有关 class 的信息:
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
项。
从this question我了解到
- 结构可以分配在栈上或寄存器中而不是堆上
- 如果结构是堆上引用类型对象的一部分,则该结构也将在堆上
但是一个结构不是对象的一部分,而是 class 的静态成员,像这样:
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 创建一个 class 的实例。如前所述,我知道作为对象的一部分存储的结构在堆上。我的代码没有创建 class. 的实例
- this question 无论它是否是静态的,它都保持打开状态,答案是“不,如果你在 Main 内部这样做,一般来说,它不会在堆上分配。”
- this question 有 Jon Skeet 的一个很好的答案,它说每个
new
在堆栈上分配 space。
也许您错过了:Eric Lippert 在旁注中提到了它:
[...] and static variables are stored on the heap.
这是在
的上下文中写的The truth is that this is an implementation detail [...]
Here's how the Microsoft implementation does it:
但是为什么静态变量存储在堆上呢?
好吧,即使是 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, which is a free debugger provided by Microsoft. It's not convenient to use, though. I learned about it from the book "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
搜索 class:
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
获取有关 class 的信息:
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
项。