【问题标题】:Is it possible to force an object onto the large object heap (LOH) even if it does not exceed 85,000 bytes?即使不超过 85,000 字节,是否可以将对象强制到大对象堆 (LOH) 上?
【发布时间】:2021-12-29 18:37:06
【问题描述】:

我有一种情况,我分配了大量内存,但在许多较小的子阵列中。我注意到,一旦我通过每个数组大约 85,000 字节的阈值,性能就会明显变差。我假设性能下降是因为较小的数组被分配在“小对象堆”(SOH)而不是“大对象堆”(LOH)上。

# of Arrays Size of Each Array (bytes) Allocation Time
1,154 532,480 377ms
1,319 465,920 412ms
1,539 339,360 439ms
1,847 332,800 435ms
2,308 266,240 446ms
3,077 199,680 491ms
4,616 133,120 514ms
9,231 66,560 4420ms

请注意,在所有情况下,分配的总内存约为 586MB,但在最后一种情况下,分配所需的时间要长一个数量级。

我想快速解决这个性能问题的第一个想法是以某种方式告诉 C# 运行时我想要大型对象堆中的这些数组即使它们小于阈值。我认为这将使分配时间与其他情况保持一致。

但是,我似乎无法找到是否有办法做到这一点。看起来以前没有人想要一个对象进入大对象堆。所以,我问:是否有可能以某种方式标记这些数组以强制它们进入大型对象堆,即使它们小于 85,000 字节的阈值?

(将 85,000 字节阈值降低到 65,000 字节的方法也可以解决我的问题,但我也找不到解决方法!)

【问题讨论】:

  • 这就像逃离马戏团加入孤儿院(又名对面土地)。分配问题、GC 压力和收集时间有很多解决方案。没有一个通常涉及射击你试图拯救的人质。
  • 你有没有尝试分配 9231 个更大的数组?可能不是大小而是触发某些事情的对象计数(例如垃圾收集或其他内务处理)。
  • @Peter-ReinstateMonica 是的,分配 9231 个长度为 85000 的数组需要大约 670 毫秒。
  • 我从未见过降低 LOH 截止限制的设置。但是,我更关心这是一个 X/Y 问题。如果这些是短暂的数组,您是否考虑过 ArrayPool ?如果您确实走上了 LOH 路径,迟早您可能会遇到更多问题,例如内存碎片等
  • 您能说明一下您是如何测量分配时间的吗? (和分配代码)。我会说,如果测试写得不好,你最终不仅可以测量分配,还可以测量 SOH 案例的 GC。

标签: c# .net large-object-heap


【解决方案1】:

在阅读https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap 之后,我假设从堆第 0 代开始的小对象会受到更频繁的垃圾回收。由于仍被引用而未收集的那些可能会被移动以进行压缩。

大型对象堆上的对象被视为类似于第 2 代对象,并且从一开始就不太频繁地收集。当它们被移动时,其余的不会移动,因为它被认为太贵了。 (在阅读您的评论后,您已经保留了对每个数组的引用,小对象的问题可能是为了压缩而移动。)

解决方案:通过保留对小对象的引用来防止垃圾回收,并固定它们以防止在收集其他对象时移动以进行压缩。

(本质上禁用垃圾收集是否可行或可取取决于具体情况。您基本上只能靠自己进行内存管理,并且必须在出现这种情况时取消固定(并且,如果到期,取消引用)您要制作的对象它们可用于收集或压缩,否则您将遇到内存或碎片问题。)

【讨论】:

    猜你喜欢
    • 2021-05-27
    • 2010-10-16
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    • 2016-02-11
    • 2017-12-18
    • 2023-01-31
    相关资源
    最近更新 更多