静态字段在内部如何工作?

Kor*_*gay 56 php c# java oop static-members

说你有课,

class Foo
{
    public static bar;
}
Run Code Online (Sandbox Code Playgroud)

当你说:

new Foo();
Run Code Online (Sandbox Code Playgroud)

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

......当你再说一次时:

new Foo(); 
Run Code Online (Sandbox Code Playgroud)

...现在你有另一个可用于该物体的空间.

但是,静态场的确切位置在哪里?

我真正想要学习的是:

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

Dan*_*ker 105

虽然类型系统的确切细节是依赖于实现的,但让我更详细地说明它取决于它并且您不应该关心.我将根据Jeffrey Richter的书籍CLR by C#Hanu Kommalapati等人的CLR创建运行时对象的文章描述它在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);
Run Code Online (Sandbox Code Playgroud)

实例字段在哪里生效?

无论何时说new Foo(),都为对象实例分配和初始化空间,并调用构造函数.此实例在下图中显示为Foo的实例.如实例只包含类(在这种情况下的实例字段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结构.

  • @KorayTugay在上面的代码中,两个字段都设置为字符串`"Foobar"`.由于[_string interning_](https://en.wikipedia.org/wiki/String_interning),它们将指向相同的String实例. (6认同)
  • 你能解释一下为什么来自Foo实例的字符串myBar的箭头和来自Foo类型的静态字符串bar会以某种方式遇到并指向String的实例吗? (2认同)
  • @Agent007同步块索引是同步块表中对象的同步块的索引.并非所有对象都有同步块.同步块包含(除其他外)对象的哈希码以及是否已在对象上获取锁. (2认同)

Ree*_*sey 16

这完全取决于有问题的实施.对于C#和Java,允许运行时确定存储变量内存的位置.对于C和大多数编译语言,编译器进行此确定.

话虽这么说,但实际上并不重要.它由规范确定的用法,因此您可以自由地使用知道行为的变量.


ass*_*ias 6

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

堆是运行时数据区,从中分配所有类实例和数组的内存.

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

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

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


Jer*_*all 5

这种语言因语言而异,甚至可能因平台而异.

例如,在.NET端,静态成员与管理EEClass定义"关联",管理定义可以是堆分配的OR或 "任何地方"分配的成员(C#规范不指定堆/堆栈行为,它是一个实现VM的细节)


Jen*_*ter 5

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

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

  • 当实际使用类型时,按需调用静态构造函数...... (3认同)

Oli*_*bes 5

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

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

它只是静态的参考; 此区别不适用于引用的对象(除非该对象是值类型).静态成员,实例成员和局部变量都可以引用同一个对象.