【问题标题】:Heap Memory Management .Net堆内存管理.Net
【发布时间】:2012-01-18 18:06:46
【问题描述】:

当创建 2 个相同类型的对象时,堆栈内存中的句柄是指向堆中的同一个对象还是指向 2 个单独的对象。为了清楚起见,这里是具体问题...

class Q2 {
   private static int num = 0;
   private String prefix;

   public Q2 (String p)
    { prefix = p; }

   public String Msg (String str) {
      String n;
      num++;   
      n = num.ToString();
     return n + " - " + prefix + str;
   }
}

使用适当的图表,描述执行完以下所有语句后的内存状态。

 Q2 var1, var2;
   var1 = new Q2("Question 2");
   var2 = new Q2 ("Another view");

以下是我无法决定的答案:

1 个对象:

2 个对象:

【问题讨论】:

  • 想想吧。不是静态类,怎么可能两个实例共享同一个内存空间呢?
  • 既然如此,num是一个静态字段。那会在记忆中的什么地方?
  • +1,因为这是一个完全有效的问题。
  • 静态变量存储在称为“高频堆”的特殊堆中。请在下面查看我的答案。

标签: c# .net memory heap-memory stack-memory


【解决方案1】:

.Net 永远不会自动组合相似的对象。

【讨论】:

    【解决方案2】:

    您正在使用new 关键字在两个单独的变量中实例化对象,因此,这总是在堆上创建新对象。所以答案是它总是指向两个独立的对象。

    编辑: num 等静态变量存储在堆上名为 High Frequency Heap 的特殊区域中,垃圾收集器等不会收集该区域。

    【讨论】:

    • 既然如此,num是一个静态字段。那会在记忆中的什么地方?
    • 这不是真的。静态变量只是引用。它们引用的对象是普通(但有根)对象。
    • @SLaks - 是的,这是真的。它们存储在称为高频堆的内存区域中。 stackoverflow.com/questions/4405627/high-frequency-heap
    • @bojanskr:我没有看到任何这样的说法。你的意思是说SomeClass.SomeField = this; 将移动this 到不同的堆?我觉得这很难相信。
    • SSCLI 书上说 MethodTables 存储在高频堆中;这与静态字段引用的值无关。
    【解决方案3】:

    From MSDN:

    无论创建了多少个类实例,静态成员都只存在一个副本。

    因此,由于您的类是非静态的,因此您最终会得到类的多个实例,但它们将引用同一个、单个、共享的静态成员实例。

    注意:您应该非常小心上述代码,因为您最终可能会出现多个类实例更改共享静态成员的值,这可能导致无法预料的行为、竞争条件、损坏等。

    如果您的意图是让该类成为共享单例,则将该类本身标记为静态,以便您在任何时候都只有一个在您的堆中。

    【讨论】:

    • 需要说明的是,每个应用程序域只存在一个静态成员的副本。
    【解决方案4】:

    为了帮助澄清这里关于堆的讨论,CLR 使用了大约 8 种不同的堆:

    1. 加载程序堆:包含 CLR 结构和类型系统
    2. 高频堆:静态、MethodTables、FieldDesc、接口映射
    3. 低频堆:EEClass、ClassLoader 和查找表
    4. 存根堆:用于 CAS、COM 包装器、P/Invoke 的存根
    5. 大型对象堆:需要超过 85k 字节的内存分配
    6. GC 堆:用户分配的应用程序专用的堆内存
    7. JIT 代码堆:由 mscoreee(执行引擎)和托管代码的 JIT 编译器分配的内存
    8. 进程/基堆:互操作/非托管分配、本机内存等

    HTH

    【讨论】:

    • 不幸的是,在所有不同的堆上都没有很多。通常,大多数人提到或知道的“堆”是“GC 堆”和 LOH。通过使用 WinDbg 和分析内存转储,我发现了很多。我发现关于堆的一些摘要信息的一个地方是msdn.microsoft.com/en-us/magazine/cc163791.aspx
    • 由于 P/Invoke 有一个特殊的堆,我想这意味着如果您在通过 P/Invoke 从托管代码调用时在 C++ 代码中分配内存,则此内存将分配在存根堆上,并且不在 GC 堆中。 IE。在 C++ 中的“堆”(存根堆)上分配内存应该是完全安全的,因为 GC 不会触及它(并压缩它)。正确的?但是如果堆内存用完了怎么办?我猜 CLR 会自动扩展它吗?
    • @DaveBlack 该链接已损坏。能否请您分享文章名称和 MSDN 问题(月和年)?另外,我有一个相关的问题:stackoverflow.com/q/43419268/303685
    • @WaterCoolrv2 - 好像是 2005 年 5 月 - “深入了解 .NET Framework 内部结构以了解 CLR 如何创建运行时对象” - 通过 Wayback Machine web.archive.org/web/20080919091745/http://msdn.microsoft.com:80/…
    • @DaveBlack 谢谢你,真是巧合。在过去 24 小时内,当我搜索可以解释加载程序堆内容的材料时,我不知何故通过几个链接找到了那篇文章。 :-)
    猜你喜欢
    • 2011-11-11
    • 1970-01-01
    • 2021-04-28
    • 2012-03-07
    • 2020-10-10
    • 1970-01-01
    • 2011-01-05
    • 2011-01-16
    • 2017-04-28
    相关资源
    最近更新 更多