所有静态成员都存储在哪里?

Ali*_*sad 39 .net c# heap stack static

我正在尝试学习C#如何管理内存.我坚持使用静态元素,我阅读了很多关于这个主题的博客和文章,但我找不到一个相当令人满意的答案.

让我们定义一个代码块来帮助找到答案.

class myClass
{
    static string myStr = "String Data";
    static int myInt = 12;
}
Run Code Online (Sandbox Code Playgroud)

在你们分享你的答案之前,让我分享一下我对这个主题的了解.随意同意或不同意,并帮助我找到正确的答案.

  • 静态只是一生.
  • 静态引用类型(myStr)将在堆上运行一生.
  • 静态值类型(myInt)将在堆栈中持续一生.

让我感到困惑的是,我在互联网上找到的关于这个主题的一些答案.

混乱1号:

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

在上面的行中,明确提到存储在AppDomain上的所有静态元素.那么为什么互联网上的每个人都说"静态"元素存储在堆/堆栈上?

困惑2:

每个静态变量都存储在堆上,无论它是在引用类型还是值类型中声明.

如果每个静态变量都存储在堆上.那么为什么有些人说值类型静态变量存储在堆栈上?

请帮助我连接点以了解C#中静态变量的内存管理.非常感谢你宝贵的时间:)

Lua*_*aan 57

首先,请注意所有这些都是实现细节.运行时唯一保证的是:

  • 当你要求静态场时,就在那里
  • 在使用该类型之前,会在某个时刻执行静态构造函数

这就是它.其他一切都是一个实现细节 - 规范不关心堆栈,堆或其他任何东西.这取决于运行时的实现,并且有效的运行时可以将所有内容放在堆栈上(如果需要的话)或堆上.不要忘记寄存器.

现在,让我们看看你已经设法提出的一些误解:

  • 静态只是一生 - 是的.它没有说明存储的时间和地点 - 只是当你要求时它可用.兼容的运行时可以自由地使用它想要的任何内存,甚至永远不会在内存中加载字段(例如,将它保存在图像中,无论如何已经在内存中)
  • 静态将在堆上,终身 - 很可能是,是的.但它不是规范的一部分,并且兼容的运行时可以将它存储在任何需要的位置,或者根本不存在,只要适当的保证保持不变.此外,不要忘记"终身"意味着"至少在AppDomain的生命周期"; 卸载域时可能会也可能不会释放它.
  • 静态值类型将在堆栈中持续一生 - 最有可能的是,没有.同样,一个实现细节,但堆栈具有完全不同于静态值的语义.下一点将为您提供更多理由:
  • 加载assambly时,将调用所有静态构造函数,包括静态字段. - 不.没有这样的要求,也没有这样的保证.如果你依赖于此,你的程序将会破裂(我以前见过很多次).同样,一个实现细节,但在当前的MSCLR实现中,静态往往被分配在它们自己的堆中,并且需要在它们被定义的类型之前的某个时间.如果你在静态构造函数中抛出异常,你可以很容易地看到它 - 它会导致a TypeLoadException,最有可能在一个首先引用该类型的方法中(不用说,这会使调试静态变得棘手).
  • 引用类型在堆上,值类型在堆栈上. - 不.这使机制与语义混淆.两者之间的唯一区别是它们的语义 - 其他一切都取决于实现.如果运行时可以保留堆栈上引用类型的引用语义,那就完全有效了.即使使用当前的MSCLR运行时,值类型也始终存储在堆上 - 例如,只要它们被装箱,或者是引用类型的成员.

有些人可能会感到困惑.有些人不理解合同和实际实施之间的区别.有些人根本不知道他们在谈论什么.我希望有一个简单的方法可以知道哪个是哪个,但没有.如果有疑问,您可以转到C#/ CLR规范,但这只会告诉您合同,而不是实际情况.

托管内存的重点在于您不应该关心这些实现细节.当然,就像任何抽象一样,它会泄漏 - 通过所有不同的层和抽象来了解事物的真实情况,直到CPU微指令,内存缓存等.但是没有什么可以依赖的 - 实施可以随时改变,而且过去有很多次.

  • @卢安。我对这个主题做了一点研究。我发现有 8 种类型的堆。其中两个是**高频堆和低频堆**。所有静态成员都位于高频堆中。我们用来存储引用类型对象的堆实际上是一个低频堆。有关更多信息,请访问 [此处](https://web.archive.org/web/20140724084944/http://msdn.microsoft.com/en-us/magazine/cc163791.aspx) 和 [此处](https: //vivekcek.wordpress.com/tag/stub-heap/) (2认同)

Ami*_*ich 6

每当进程加载到RAM中时,我们可以说内存大致分为三个区域(在该进程中):Stack,Heap和Static(在.NET中,实际上是Heap中的一个特殊区域,仅称为高频堆).

静态部分包含"静态"成员变量和方法.什么是静态的?那些不需要创建类实例的方法和变量被定义为静态的

在这里阅读更多.