【问题标题】:C# static and const variables memoryC# 静态和常量变量内存
【发布时间】:2014-02-16 08:28:13
【问题描述】:

对 C# 静态/常量成员/局部变量有疑问。只想知道在C#中分配给未使用的静态/常量成员/局部变量的内存会发生什么,下面的示例场景中的内存是如何回收的?

问题是关于静态和常量变量的内存行为[考虑每个应用程序域的内存-静态存储]?这个问题与垃圾收集无关。这是关于内存以及(也)有内存的未使用的静态和常量变量会发生什么?

意大利面条代码片段:

/// <summary>
/// Skew your data with every-second-and-annoyed updates
/// </summary>
class Skewgle
{
    static Skewgle cloneApele = new Skewgle();
    const Skewgle patentMoto = default(dynamic);
    static int? dontBeEvilMotto = 1998;
    const int ditchMotoToBeEvil = 2014;

    static void Main()
    {
        const Skewgle findYourMailsAlreadyReadBetweenSpamTabs = patentMoto;

        if (findYourMailsAlreadyReadBetweenSpamTabs == null)
        {
            System.Console.WriteLine("findYourMailsAlreadyReadBetweenSpamTabs and patentMoto are null");
        }

        if (cloneApele != null)
        {
            System.Console.WriteLine("cloneApele is not null");
        }

        System.Console.WriteLine("What about dontBeEvilMotto? ditchMotoToBeEvil?");
    }
}

谢谢

【问题讨论】:

  • 按建议改写。顺便说一句,Alexei Levenkov 和 Quetzalcoatl 已经很好地回答了这个问题。大佬们点赞!!!

标签: c#


【解决方案1】:

所有类的静态字段将在第一次使用该类的任何实例/静态方法之前被初始化 (static field initialization in C# spec)。

静态字段是每个应用程序域的,并且在卸载 AppDomain 之前不会为 GC 标记值。

【讨论】:

    【解决方案2】:

    MSDN:http://msdn.microsoft.com/en-us/library/e6w8fe1b.aspx

    常量表达式是可以在编译时完全计算的表达式。因此,引用类型常量的唯一可能值是 string 和 null。

    所以你不能让patentMotonull 以外的任何东西。顺便提一句。 default(dynamic) 完全返回 null。您可以在那里简单地写=null,这样阅读起来会更简单。除了string,您将永远不会拥有任何非空“常量对象变量”。但是你可以拥有大量的statics。

    conststatic 在内存中的 AppDomain 被卸载之前永远不会被 GC。

    根据定义,GC 会清理不再引用的所有内容。由于静态变量会记住某个对象,因此只要存在 static 变量,该对象就不会被清除。只要包含类型“存在”,它就存在,因此只有在卸载 AppDomain 时它才不再存在。除非您创建自己的额外应用程序域并在某个时间“手动”卸载它们,否则这意味着它们会在程序退出时被清除。

    但是,这指的是自动清理static 变量记住的东西。你可以早点发布这些东西——你只需要nullify 静态变量。 GC 扫描对象,而不是变量。 (*)

    显然,const 是不可能的,所以任何大的const 字符串都会永远占用内存。而关于 AppDomain - 对于const,它更难:它们实际上被编译到程序集中。因此,只有在程序集从内存中卸载后,它们才会被完全清理。如果程序集在 appdomains 之间共享,那么它将一直保留到最后一个被删除。 Const 是只读且不可变的,因此无论如何共享它应该不会很痛苦。请记住,所有对象 const 变量都是 null 或字符串。没有其他选择。因此,除非您创建一个 4 Gb 的 const 字符串,否则您不必担心这一点。

    const 在本地范围内没有任何区别。它是一个本地“变量”,但它仍然编译到原始程序集中。见上文。

    (*) 这意味着如果您创建一个包含一百万 (1000000) 个静态变量的类,全部为空,那么它们的存在将占用至少 ~4MB 的内存,直到应用程序域被卸载。那是因为要保留一百万个空指针。在这种情况下没有 GC,只有 Type 本身。

    【讨论】:

    • 感谢您的意见和回答。
    【解决方案3】:

    const 和 static 的主要区别在于内存的位置。 所有 const 数据都位于与只读存储器 (ROM) 的可执行代码相同的内存空间中,其中静态数据被加载到动态内存中,您可以在其中潜在地对其进行读写。 现在这里是有趣的部分,似乎在之前的每个答案中都被忽略了。 静态数据占用动态内存空间,但需要额外的 ROM 空间,在最坏的情况下,可能会占用两倍的内存量。 考虑以下几点:

    class StaticData
    {
        static readonly int s_static1 = 1;
        static readonly int s_static2 = 2;
    }
    
    class ConstData
    {
        const int CONST1 = 1;
        const int CONST2 = 2;
    }
    

    StaticData 类有两个静态变量 s_static1 和 s_static2,它们将占用 2 个整数的动态内存空间。但是它们都需要初始化,因此还必须存在 ROM 代码来初始化它们。在这个例子中,根据编译器的不同,至少需要 2 个字节(或 2 个没有优化的存储整数)的 ROM 空间来存储用于初始化静态变量的 2 个常量值(1 和 2),而不是提到初始化它们所需的可执行ROM代码。

    对于 ConstData 类,只需要 ROM 存储空间,因此在大多数情况下,这是对内存的最佳使用。

    现在,当您考虑编译器如何使用数据时,它会变得更加有趣。除字符串/字符数据外,常量通常在引用点的代码中直接替换。换句话说,常量值直接加载到寄存器或压入堆栈,具体取决于使用情况。在静态变量的情况下,编译器必须通过附加代码(指针引用)从内存中读取该值,因此需要附加 ROM 代码。

    所以总而言之,使用“const”代替“static readonly”几乎总是更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-14
      • 1970-01-01
      • 1970-01-01
      • 2012-12-10
      • 2013-11-06
      • 2010-12-09
      • 2013-06-08
      • 1970-01-01
      相关资源
      最近更新 更多