代码之家  ›  专栏  ›  技术社区  ›  CocoaMix86

变量超出作用域后未回收内存[重复]

  •  0
  • CocoaMix86  · 技术社区  · 5 月前

    我读过关于世代和大对象堆的文章,但我仍然不明白拥有大对象堆有什么意义(或好处)?

    如果CLR仅依赖第2代来存储大型对象(考虑到Gen0和Gen1的阈值太小而无法这样做),那么可能会出现什么问题(在性能或内存方面)?

    0 回复  |  直到 2 年前
        1
  •  230
  •   Hans Passant    11 年前

    垃圾回收不仅可以清除未引用的对象,还可以 压实机 堆。这是一个非常重要的优化。它不仅使内存使用更高效(没有未使用的漏洞),还使CPU缓存更高效。缓存在现代处理器上非常重要,它们比内存总线快一个数量级。

    压缩只需复制字节即可完成。然而,这需要时间。对象越大,复制它的成本就越有可能超过CPU缓存使用率的可能改善。

    因此,他们运行了一系列基准测试来确定盈亏平衡点。达到85000字节作为复制不再提高性能的临界点。除了double数组的特殊例外,当数组包含1000多个元素时,它们被认为是“大”的。这是32位代码的另一个优化,大对象堆分配器具有特殊属性,它在与8对齐的地址处分配内存,这与只分配与4对齐的常规分代分配器不同。这种对齐对于double来说是一件大事,读取或写入对齐错误的double非常昂贵。奇怪的是,稀疏的微软信息从未提到长数组,不知道这是怎么回事。

    Fw,有很多程序员担心大型对象堆没有被压缩。当他们编写的程序消耗了整个可用地址空间的一半以上时,这总是会被触发。随后,使用内存分析器等工具找出程序崩溃的原因,即使还有大量未使用的虚拟内存可用。这样的工具显示了LOH中的漏洞,即以前有一个大型对象存在但被垃圾收集的未使用的内存块。这就是LOH的不可避免的代价,这个洞只能通过为大小相等或更小的物体分配来重新使用。真正的问题是假设一个程序应该被允许消费 全部的 虚拟内存随时可用。

    只需在64位操作系统上运行代码,这个问题就会完全消失。64位进程具有 8TB 虚拟内存地址空间比32位进程多3个数量级。你就是跑不出洞。

    长话短说,LOH使代码运行更高效。以使用可用虚拟内存地址空间效率较低为代价。


    更新。NET 4.5.1现在支持压缩LOH, GCSettings.LargeObjectHeapCompactionMode 财产。请小心后果。

        2
  •  16
  •   Pavel Chuchuva grapeot    8 年前

    小对象堆(SOH)和大对象堆(LOH)的本质区别在于,SOH中的内存在收集时会被压缩,而LOH则不会,因为 this article 说明。压缩大型物体的成本很高。与本文中的示例类似,假设在内存中移动一个字节需要2个周期,那么在2GHz计算机中压缩8MB对象需要8ms,这是一个很大的成本。考虑到大型对象(在大多数情况下是数组)在实践中非常常见,我想这就是微软将大型对象固定在内存中并提出LOH的原因。

    顺便说一句,根据 this post ,LOH通常不会产生内存碎片问题。

        3
  •  11
  •   oleksii    9 年前

    如果对象的大小大于某个固定值(.NET 1中为85000字节),则CLR会将其放入大对象堆中。这优化了:

    1. 对象分配(小对象不与大对象混合)
    2. 垃圾回收(仅在完整GC上收集LOH)
    3. 内存碎片整理(LOH是 从未 很少压实)
        4
  •  5
  •   mjwills Myles McDonnell    5 月前

    其原理是,一个进程不太可能(也很可能是糟糕的设计)创建大量生命周期较短的大型对象,因此CLR将大型对象分配给一个单独的堆,并在其上以与常规堆不同的计划运行GC。 http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

        5
  •  0
  •   Chris Shain    13 年前

    我不是CLR方面的专家,但我认为为大型对象设置一个专用堆可以防止对现有世代堆进行不必要的GC扫描。分配大型对象需要大量 连续 免费内存。为了从世代堆中分散的“洞”中提供这一点,你需要频繁的压缩(这只在GC循环中完成)。