我在dotmemory中分析了Windows窗体应用程序的内存使用情况,我发现在我的应用程序中,有大量不同大小的0-4堆以及大对象堆。
我只是想知道是否有人对每个堆的用途以及通常存储在每个堆中的内容有一个很好的解释?
答案 0 :(得分:12)
其他答案似乎忽略了堆和代之间存在差异的事实。我不明白为什么商业分析师会混淆这两个概念,所以我强烈怀疑它是堆积而不是几代人。
当CLR GC使用server flavor时,它会为进程的关联掩码中的每个逻辑处理器创建一个单独的堆。这种分解的原因主要是为了提高分配的可扩展性,并在GC中并行执行。这些是单独的内存区域,但您当然可以在堆之间具有对象引用,并且可以将它们视为单个逻辑堆。
因此,假设您有四个逻辑处理器(例如,启用了超线程的i5 CPU),您将在服务器GC下有四个堆。
大对象堆有一个令人遗憾,令人困惑的名字。它不是与处理器堆叠相同的意义上的堆。它是包含大对象的多个内存区域之上的逻辑抽象。
答案 1 :(得分:8)
由于C#垃圾收集器的工作原理,你有不同的堆。它使用分代GC,根据最近使用的数据分离数据。使用不同的堆允许垃圾收集器更有效地清理内存。
根据MSDN:
堆被组织成几代,因此它可以处理长寿命和短期对象。垃圾收集主要发生在回收通常仅占用堆的一小部分的短期对象时。
- 第0代。这是最年轻的一代,包含短期对象。短期对象的示例是临时变量。垃圾收集在这一代中最常发生。 新分配的对象形成新一代对象,并且隐式生成0集合,除非它们是大对象,在这种情况下它们在第2代集合中的大对象堆上。 大多数对象都被回收用于第0代的垃圾收集,并且不能存活到下一代。
- 第1代。这一代包含短期对象,并作为短期对象和长寿命对象之间的缓冲区。
- 第2代。此代包含长寿命对象。长期对象的一个示例是服务器应用程序中的一个对象,该对象包含在该过程期间处于活动状态的静态数据。
垃圾收集中未回收的对象称为幸存者,并被提升为下一代。
重要数据很快就会被放到垃圾收集器的后烧器(更高代)上,并且不经常检查删除。这样可以减少浪费的时间,检查真正需要持久存储的内存,从而可以从高效的垃圾收集器中看到性能提升。
答案 2 :(得分:1)
对于托管对象,有三个小对象堆(SOH)和一个大对象堆(LOH)。
大对象堆(LOH)
大于85KB的物体将立即进入LOH。如果您有太多大型物体,则存在一些风险。这是一个不同的讨论,有关详细信息,请查看The Dangers of the Large Object Heap
小对象堆(SOH):Gen0,Gen1,Gen2
垃圾收集器使用一种聪明的算法仅在需要时执行垃圾收集器。完全垃圾收集过程是一项昂贵的操作,不应该经常发生。因此,它已将其SOH分为三个部分,因为您已经注意到每个Gen都有指定的内存量。
每个小对象(<85KB)最初都会转到Gen0。当Gen0已满时,垃圾收集仅对Gen0执行。它检查Gen0中的所有实例并清除/释放任何不必要的对象(未引用的,超出范围或处置的对象)使用的内存。然后它将所有必需的(已使用的)实例复制到Gen1。
即使您执行以下操作,上述过程实际上也会发生:(不需要手动调用)
// Perform a collection of generation 0 only.
GC.Collect(0);
通过这种方式,垃圾收集器首先清除为短期实例分配的内存(不可变的字符串,方法中的变量或较小的范围)。
当GC在一个阶段继续执行此操作时,Gen1溢出。然后它对Gen1执行相同的操作。它清除Gen1中所有不必要的内存,并将所有必需的内存复制到Gen2。
当您在下面手动执行(不需要手动调用)
时,会发生上述过程// Perform a collection of all generations up to and including 1.
GC.Collect(1);
当GC在一个阶段继续执行此操作时,如果Gen2溢出,它会尝试清除Gen2。
即使您在下面手动执行(不需要手动执行)
,也会发生上述过程// Perform a collection of all generations up to and including 2.
GC.Collect(2);
如果需要从Gen1复制到Gen2的内存量大于Gen2中可用的内存量,GC会抛出内存不足。