66

假设你有一堂课,

class Foo
{
    public static bar;
}

当你说:

new Foo();

我可以想象在内存中,为这个对象保留了一个空间。

...当你再说一遍:

new Foo(); 

...好吧,现在您有另一个空间可用于该对象。

但是,静态场究竟在哪里?

我真正想学习的是:

对对象的引用如何引用它们引用的对象的相同字段?

4

11 回答 11

118

虽然类型系统的确切细节取决于实现,但让我更详细地说明它,而不是仅仅说明它取决于并且你不应该关心我将根据Jeffrey Richter的CLR 通过 C#一书和Hanu Kommalapati 等人的文章See How the CLR Creates Runtime Objects描述它在 Microsoft 的实现 (.NET) 中的大致工作原理。(原始 MSDN 2005 年 5 月号)。


假设你有一堂课:

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);

实例字段在哪里?

每当您说new Foo()时,都会为对象实例分配和初始化空间,并调用构造函数。此实例在下图中显示为Foo 的实例。例如 instance 仅包含类的实例字段(在本例中为myBarmyNum),而对于分配在堆上的对象,运行时使用了两个额外的字段(Sync block indexType handle)。类型句柄是指向Type描述实例类型的对象的指针,在本例中为 Foo 类型

当您new Foo()再次说时,分配了新空间,该空间将再次包含该类型的实例字段的空间。如您所见,实例字段与对象实例相关联。

运行时将每个实例字段放置在距对象数据开头的固定偏移处。例如,myBar可能生活在偏移量 +4。实例字段的地址就是对象的地址加上字段的偏移量。

静态字段在哪里?

C# 和 Java 中的静态字段不与任何对象实例相关联,而是与类型相关联。类、结构和枚举是类型的示例。只有一次(每种类型)分配了一些空间来保存静态字段的值。为描述类型的结构中的静态字段分配空间是有意义的,因为每种类型Type也只有一个对象。Type这是 C# 和 Java 采用的方法。

Type对象1是在运行时加载类型时创建的。此结构包含运行时所需的各种信息,以便能够分配新实例、调用方法和执行强制转换等。它还包含静态字段的空间,在本例中为barnum

运行时已将每个静态字段放置在与类型数据开头的某个偏移量处。这对于每种类型都是不同的。例如,bar可能生活在偏移 +64。静态字段的地址是Type对象的地址加上字段的偏移量。类型是静态已知的。

显示一些对象结构及其关系。

1 ) 在 Microsoft .NET 中,多种不同的结构描述了一个类型,例如MethodTableEEClass结构。

于 2013-03-02T18:10:11.380 回答
16

这完全取决于所讨论的实现。对于 C# 和 Java,运行时可以决定在哪里存储变量的内存。对于 C 和大多数编译语言,编译器会做出此决定。

话虽如此,在实践中,这并不重要。它由规范确定的用法,因此您可以自由使用知道行为的变量将得到保证。

于 2013-02-08T22:02:57.793 回答
6

对于 Java,静态字段引用的对象将像其他对象一样驻留在堆上:

堆是为所有类实例和数组分配内存的运行时数据区域。

当类被加载时,该字段将被初始化(如果声明包含初始化),这发生在以下任何一项第一次出现之前:

  • 类的一个实例被创建。
  • 调用类声明的静态方法。
  • 分配了类声明的静态字段。
  • 使用类声明的静态字段,并且该字段不是常量变量(第 4.12.4 节)。

对静态字段的访问是通过 2 个特殊的 JVM 指令getstaticputstatic完成的。但除了这种区别之外,静态字段类似于非静态字段。

于 2013-02-26T20:02:33.923 回答
5

我只熟悉C#,这是我对它的理解:

然后您的程序启动,它将所有相关程序集加载到 AppDomain 中。加载组件时,会调用所有静态构造函数,包括静态字段。他们将在那里生活,卸载它们的唯一方法是卸载 AppDomain。

于 2013-02-08T22:05:28.620 回答
5

这因语言而异,甚至因平台而异……

例如,在 .NET 方面,静态成员与管理EEClass定义“关联”,可以是堆分配的“任何地方”分配的成员(C# 规范没有指定堆/堆栈行为,它是一种实现虚拟机的详细信息)

于 2013-02-08T22:03:01.463 回答
5

可能会有例外,但对于引用类型,new-keyword 通常会在称为“堆”的内部数据结构中创建一个对象。堆由 CLR(公共语言运行时)管理。无论您拥有静态成员、实例成员还是局部变量,都没有区别。

静态成员和实例成员(没有关键字的成员)之间的区别在于static,静态成员每个类型(类、结构)仅存在一次,而实例成员每个实例(每个对象)存在一次。

只有参考是静态的还是非静态的;这种区别不适用于被引用的对象(除非对象是值类型)。静态成员、实例成员和局部变量都可以引用同一个对象。

于 2013-02-26T18:24:06.113 回答
3

Static members and constants are stored on heap. Unlike objects on heap that can get garbage collection, Static members and constants do stay till Appdomain is torn down, Hence once should be carefull in dealing with static fields

于 2013-04-04T14:33:11.387 回答
2

静态变量属于类而不是对象,因此bar即使您初始化了数千个Foo.

于 2013-02-08T22:02:50.080 回答
1

根据规范,静态变量存储在Constant Pool中。JVM 将此信息存储在永久代中。

于 2013-03-03T10:01:52.590 回答
1

这取决于语言到语言或语言的设计者。如果我谈论 Java,静态成员存储在 JVM 的方法区域中,并且所有对象都链接到它们。另一件非常重要的事情是我们可以在不创建类的对象的情况下访问静态数据成员。这意味着分配给静态数据成员的内存不依赖于该对象的创建班级。

于 2013-02-27T02:34:47.353 回答
0

通常静态变量存储在程序存储器的数据段中。因此,对于正在运行的程序中创建/存在的每个类,都将在数据段上创建静态变量,并且所有其他变量都在代码段中初始化。

所以基本上就像

+++++++++++++++++++++++++++++++++++++
+ DATA Segment
+   -static vars
+   
+----------------------------------
+  instances | instances | instances|
+            |           |          |

这里单个区域在实例之间共享。

来自维基百科 “数据区域包含程序使用的全局变量和静态变量,这些变量
显式初始化为一个值。”

于 2013-03-05T14:29:28.740 回答