假设你有一个班级,
class Foo
{
public static bar;
}
当你说:
new Foo();
我可以想象在内存中,为这个对象保留了一个空间。
......当你再说一次时:
new Foo();
......现在你有另一个可用于该物体的空间。
然而,静态场的确切位置在哪里?
我真正想要学习的是:
对象的引用如何引用它们引用的对象的相同字段?
答案 0 :(得分:101)
虽然类型系统的具体细节是依赖于实现的,但是让我更详细地介绍一下它取决于和你不应该关心。我将根据书籍CLR via C# by Jeffrey Richter和文章See How the CLR Creates Runtime Objects by Hanu Kommalapati et al.(original MSDN May 2005 issue)描述它在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);
实例字段在哪里生效?
每当你说new Foo()
时,就会为对象实例分配和初始化空间,并调用构造函数。此实例在下图中显示为 Foo 的实例。例如实例仅包含类的实例字段(在本例中为myBar
和myNum
),对于在堆上分配的对象,运行时使用的两个额外字段(Sync block index
和{ {1}})。类型句柄是指向Type handle
对象的指针,该对象描述实例的类型,在本例中为类型的Foo 。
再次说Type
时,会分配新空间,该空间将再次包含该类型实例字段的空间。如您所见,实例字段与对象实例相关联。
运行时将每个实例字段放在距对象数据开头的固定偏移处。例如,new Foo()
可能位于抵消+4。实例字段的地址只是对象的地址加上字段的偏移量。
静态字段在哪里生效?
C#和Java中的静态字段不与任何对象实例相关联,而是与类型相关联。类,结构和枚举是类型的示例。只有一次(每种类型)分配一些空间来保存静态字段的值。为描述类型的myBar
结构中的静态字段分配空间是有意义的,因为每种类型只有一个Type
个对象。这是C#和Java采用的方法。
运行时加载类型时会创建Type
对象 1 。此结构包含运行时能够分配新实例,调用方法和执行转换等所需的各种信息。它还包含静态字段的空间,在本例中为Type
和bar
。
运行时将每个静态字段放置在距类型数据开头的某个偏移处。每种类型都有所不同。例如,num
可能位于偏移+64。静态字段的地址是bar
对象的地址加上字段的偏移量。这种类型是静态的。
1 )在Microsoft .NET中,多个不同的结构描述了一种类型,例如 MethodTable 和 EEClass 结构。
答案 1 :(得分:16)
这完全取决于有问题的实施。对于C#和Java,允许运行时确定存储变量内存的位置。对于C和大多数编译语言,编译器都会做出此决定。
话虽如此,在实践中,并不重要。它由规范确定的用法,因此您可以自由地使用知道行为的变量。
答案 2 :(得分:5)
这种语言因语言而异,甚至可能因平台而异:...
例如,在.NET端,静态成员是"关联的"使用管理EEClass
定义,其中可以是一个堆分配的 OR a"无论在哪里"已分配的成员(C#规范没有指定堆/堆栈行为,它是VM的实现细节)
答案 3 :(得分:5)
可能有例外,但对于引用类型,new
- 关键字通常在名为“heap”的内部数据结构中创建一个对象。堆由CLR(公共语言运行时)管理。无论您是静态成员还是实例成员还是局部变量都没有区别。
静态成员和实例成员(没有关键字static
的成员)之间的区别在于,每个类型(类,结构)只存在一次静态成员,每个实例(每个对象)存在一次实例成员。
它只是静态的参考;此区别不适用于引用的对象(除非该对象是值类型)。静态成员,实例成员和局部变量都可以引用同一个对象。
答案 4 :(得分:5)
对于Java,静态字段引用的对象将像其他对象一样驻留在on the heap:
堆是运行时数据区,从中分配所有类实例和数组的内存。
该字段将在the class is loaded时初始化(如果声明包含初始化),该字段在第一次出现以下任何一个之前发生:
- 创建了一个类的实例。
- 调用类声明的静态方法。
- 分配由类声明的静态字段。
- 使用类声明的静态字段,字段不是常量变量(§4.12.4)。
通过2个特殊的JVM指令getstatic和putstatic来访问静态字段。但除了这种区别外,静态字段与非静态字段类似。
答案 5 :(得分:4)
我只熟悉C#,这是我对它的理解:
然后程序启动,它将所有相关程序集加载到AppDomain中。 加载assambly时,将调用所有静态构造函数,包括静态字段。他们将住在那里,卸载它们的唯一方法就是卸载AppDomain。
答案 6 :(得分:2)
静态成员和常量存储在堆上。与堆上可以进行垃圾收集的对象不同,静态成员和常量一直保持到Appdomain被拆除为止,因此曾经应该小心处理静态字段
答案 7 :(得分:1)
静态变量属于类而非对象,因此即使初始化数千个bar
时刻,内存中也只有一个Foo
。
答案 8 :(得分:1)
这取决于语言的语言或语言的设计者。如果我谈论Java,静态成员存储在JVM的Method区域中,并且所有对象都链接到它们。还有一件事非常重要,我们可以访问静态数据成员而无需创建类的对象。这意味着将内存分配给静态数据成员不依赖于对象的创建。类。
答案 9 :(得分:1)
按规范,静态变量存储在Constant Pool中。 JVM将此信息存储在Permanent Generation中。
答案 10 :(得分:0)
通常,静态变量存储在程序存储器的数据段中。 因此,对于正在运行的程序中创建/正在运行的每个类,将在数据段上创建静态变量,并在代码段中初始化所有其他变量。
所以基本上它就像
+++++++++++++++++++++++++++++++++++++
+ DATA Segment
+ -static vars
+
+----------------------------------
+ instances | instances | instances|
+ | | |
这里单个区域在实例之间共享。
来自wikipedia的“数据区包含程序使用的全局和静态变量 用值显式初始化。“