静态字段在内部究竟是如何工作的?
假设你有一堂课,
class Foo
{
public static bar;
}
当你说:
new Foo();
我可以想象,在内存中,为这个对象保留了一个空间。
...当你再次说:
new Foo();
...好吧,现在你有另一个空间可用于对象。
但是,静态场究竟位于何处?
我真正想学习的是:
对对象的引用如何引用它们所引用的对象的相同字段?
假设你有一堂课,
class Foo
{
public static bar;
}
当你说:
new Foo();
我可以想象,在内存中,为这个对象保留了一个空间。
...当你再次说:
new Foo();
...好吧,现在你有另一个空间可用于对象。
但是,静态场究竟位于何处?
我真正想学习的是:
对对象的引用如何引用它们所引用的对象的相同字段?
虽然类型系统的确切细节取决于实现,但让我更详细地介绍一下,而不仅仅是说它取决于,你不应该关心。我将根据 Jeffrey Richter 的《CLR via C#》一书和 Hanu Kommalapati 等人的文章《请参阅 CLR 如何创建运行时对象》(原始 MSDN 2005 年 5 月刊)描述它在 Microsoft 的实现 (.NET) 中大致的工作方式。
假设您有一个班级:
class Foo
{
// Instance fields
string myBar = "Foobar";
int myNum;
// Static fields
static string bar = "Foobar";
static int num;
}
Foo myFoo = new Foo();
Type typeOfFoo = typeof(Foo);
实例字段位于何处?
每当您说 ,将为对象实例分配和初始化空间,并调用构造函数。此实例在下图中显示为 Foo 的实例。例如,instance 仅包含类的实例字段(在本例中为 和 ),对于堆上分配的对象,运行时使用两个额外的字段 ( 和 )。类型句柄是指向描述实例类型的对象的指针,在本例中为 Foo 类型。new Foo()
myBar
myNum
Sync block index
Type handle
Type
当您再次说时,将分配新空间,该空间将再次包含该类型的实例字段的空间。如您所见,实例字段与对象实例相关联。new Foo()
运行时将每个实例字段置于从对象数据开始的固定偏移量。例如,可能位于偏移量 +4 处。实例字段的地址只是对象的地址加上字段的偏移量。myBar
静态字段位于何处?
C# 和 Java 中的静态字段不与任何对象实例相关联,而是与类型相关联。类、结构和枚举是类型的示例。只有一次(每个类型)分配一些空间来保存静态字段的值。在描述类型的结构中为静态字段分配空间是有意义的,因为每个类型也只有一个对象。这是 C# 和 Java 采用的方法。Type
Type
对象1 是在运行时加载类型时创建的。此结构包含运行时能够分配新实例、调用方法和执行强制转换等所需的所有信息。它还包含静态字段的空间,在本例中为 和 。Type
bar
num
运行时已将每个静态字段放在距类型数据开头的某个偏移处。这对于每种类型都是不同的。例如,可能位于偏移量 +64 处。静态字段的地址是对象的地址加上字段的偏移量。该类型是静态已知的。bar
Type
1)在Microsoft .NET中,多种不同的结构描述了一种类型,例如MethodTable和EEClass结构。
这完全取决于所讨论的实现。对于 C# 和 Java,允许运行时确定在何处存储变量的内存。对于 C 和大多数编译语言,编译器会做出此决定。
话虽如此,在实践中,这并不重要。它的用法由规范决定,因此您可以自由使用知道行为的变量将得到保证。