我知道我们不会初始化静态类。我们可以按类名使用它们。但它必须在静态内存中,以便我们可以使用它们。什么时候静态类放在静态内存中?是运行时还是延迟加载?
答案 0 :(得分:3)
我怀疑你认为它很懒惰。我不知道你对“静态记忆”是什么意思,但我认为这是一个非常可行的问题,关于静态成员的运作方式以及他们如何使用记忆。静态成员将被放置在“常规内存”中,就像任何实例成员一样。
静态实例在加载时被放置到堆上(尽管它是一种不同于实例的堆,称为High Frequency Heap),但在使用之前不会被初始化。根据您拥有的静态成员(以及它们的类型,以及这些类型是引用类型还是值类型),这可能是一个大的或小的交易。
例如,如果您有一个包含200个元素的字节数组,那么只有在您使用它时才会加载(少于它最初会保留的null
)。但是如果你有200个字节的元素,每个元素都将在开头放入内存。
换句话说,在使用之前,ClassA
占用的内存少于ClassB
,但在使用过程中,它们会相似。当然,情况并不完全相同,但你可以想象我想要做的一点。
static class ClassA
{
public static byte[] bytes = new byte[] { 1, 12 };
}
static class ClassB
{
public static byte ByteA = 1;
public static byte ByteB = 12;
}
这是因为在ClassA
中,bytes
的初始值为null
,但在ClassB
中,从流程的开始存储了两个值,这两个值都发生了是0
。你可以想象这可能会与其他参考型和价值型字段一起发挥作用。
当GC运行时,另一个区别就出现了。如果我没有弄错(尽管我不相信这是规范的行为),静态成员在进程终止之前不会被垃圾收集。除此之外,你可以(松散地)将它们视为一个简单的,编译器强制的类的单例实例。
确定值的“延迟加载”的最简单方法是在静态构造函数中放置一个副作用调用。像,
public class Foo
{
static Foo()
{
Console.WriteLine("In static constructor!");
}
public static void Bar()
{
Console.WriteLine("In Bar!");
}
}
在一个小型控制台应用程序的上下文中,您可以轻松确定何时调用构造函数,并推断这也是静态成员被放入内存的时候。
void Main(string[] args)
{
Console.WriteLine("First line!");
Foo.Bar();
Console.WriteLine("Last line!");
}
此计划outputs:
First line!
In static constructor!
In Bar!
Last line!
您也可以在C#5.0规范(§10.12静态构造函数)中找到此信息,
封闭类类型的静态构造函数在给定的应用程序域中最多执行一次。静态构造函数的执行由应用程序域中发生的以下第一个事件触发:
- 创建了类类型的实例。
- 引用类类型的任何静态成员。
在外行人的术语中,静态成员在您第一次使用静态类时初始化。如果它从未使用过,它们就永远不会被初始化。
为了让我的观点回家,让我们来看看最后一对方法。
static class Foo
{
public static int X = 2;
public static byte[] Bytes = new byte[] { 24, 66 };
}
class Bar
{
public int X = 2;
public byte[] Bytes = new byte[] { 24, 66 };
}
单步执行(在IL级别),与这些类的交互将完全不同。
考虑一种方法:
void Main(string[] args)
{
// Foo is put into memory when the process spins up:
// X : 0
// Bytes : null
Console.Write(Foo.X); // During this line, Foo is initialized
// X : 2
// Bytes : 24, 66
Console.Write(Foo.X); // Nothing happens
Bar b = new Bar(); // During this line, Bar is put into memory, then initialized
// X : 0, then "instantaneously" becomes 2
// Bytes : null, then "instantaneously" becomes 24, 66
Console.Write(b.X); // Nothing happens
// Eventually, b will be cleared from memory by the garbage collector
// but Foo will not be, until the program closes. This sample is much
// too small to display that difference.
}