为什么对象堆很大,为什么我们要关心?

时间:2021-11-26 22:26:33

I have read about Generations and Large object heap. But I still fail to understand what is the significance (or benefit) of having Large object heap?

我已经阅读了几代人和大型对象堆。但是我仍然不能理解拥有大型对象堆的意义(或好处)是什么?

What could have went wrong (in terms of performance or memory) if CLR would have just relied on Generation 2 (Considering that threshold for Gen0 and Gen1 is small to handle Large objects) for storing large objects?

如果CLR仅仅依赖于第2代(考虑到Gen0和Gen1的阈值对于处理大型对象来说很小),那么会出现什么问题(就性能或内存而言)?

5 个解决方案

#1


166  

A garbage collection doesn't just get rid of unreferenced objects, it also compacts the heap. That's a very important optimization. It doesn't just make memory usage more efficient (no unused holes), it makes the CPU cache much more efficient. The cache is a really big deal on modern processors, they are an easy order of magnitude faster than the memory bus.

垃圾收集不仅可以删除未引用的对象,还可以压缩堆。这是一个非常重要的优化。它不仅提高了内存使用效率(没有未使用的漏洞),而且使CPU缓存更加高效。缓存对于现代处理器来说是非常重要的,它们比内存总线要快得多。

Compacting is done simply by copying bytes. That however takes time. The larger the object, the more likely that the cost of copying it outweighs the possible CPU cache usage improvements.

压缩只需复制字节即可。,但是需要时间。对象越大,复制它的成本就越有可能超过可能的CPU缓存使用改进。

So they ran a bunch of benchmarks to determine the break-even point. And arrived at 85,000 bytes as the cutoff point where copying no longer improves perf. With a special exception for arrays of double, they are considered 'large' when the array has more than 1000 elements. That's another optimization for 32-bit code, the large object heap allocator has the special property that it allocates memory at addresses that are aligned to 8, unlike the regular generational allocator that only allocates aligned to 4. That alignment is a big deal for double, reading or writing a mis-aligned double is very expensive. Oddly the sparse Microsoft info never mention arrays of long, not sure what's up with that.

他们运行了一系列基准来确定盈亏平衡点。并且达到了85,000字节的截断点,复制不再提高性能。除了double数组的特殊例外,当数组中有超过1000个元素时,它们被认为是“大”的。这是32位代码的另一种优化,大型对象堆分配器具有特殊的属性,它将内存分配到与8对齐的地址,不像通常的代分配器,它只分配到4。双对齐对双对齐很重要,读或写双对齐不正确的双对齐非常昂贵。奇怪的是,稀疏的微软信息从来没有提到过长数组,不知道这是怎么回事。

Fwiw, there's lots of programmer angst about the large object heap not getting compacted. This invariably gets triggered when they write programs that consume more than half of the entire available address space. Followed by using a tool like a memory profiler to find out why the program bombed even though there was still lots of unused virtual memory available. Such a tool shows the holes in the LOH, unused chunks of memory where previously a large object lived but got garbage collected. Such is the inevitable price of the LOH, the hole can only be re-used by an allocation for an object that's equal or smaller in size. The real problem is assuming that a program should be allowed to consume all virtual memory at any time.

Fwiw,有很多程序员对大型对象堆没有被压缩感到焦虑。当他们编写的程序占用了整个可用地址空间的一半以上时,这种情况就会被触发。然后使用像内存分析器这样的工具来找出为什么程序会失败,即使仍然有许多未使用的虚拟内存可用。这样一个工具显示了LOH中的漏洞,这是一种未使用的内存块,以前大型对象在这里居住,但会收集垃圾。这就是LOH的必然价格,这个洞只能被分配给大小相等或更小的对象。真正的问题是假设一个程序应该被允许在任何时候使用所有的虚拟内存。

A problem that otherwise disappears completely by just running the code on a 64-bit operating system. A 64-bit process has 8 terabytes of virtual memory address space available, 3 orders of magnitude more than a 32-bit process. You just can't run out of holes.

在64位操作系统上运行代码就可以完全消除这个问题。64位进程有8 tb的虚拟内存地址空间可用,比32位进程多3个数量级。你就是不能没有洞。

Long story short, the LOH makes code run more efficient. At the cost of using available virtual memory address space less efficient.

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


UPDATE, .NET 4.5.1 now supports compacting the LOH, GCSettings.LargeObjectHeapCompactionMode property. Beware the consequences please.

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

#2


8  

If the object's size is greater than some pinned value (85000 bytes in .NET 1), then CLR puts it in Large Object Heap. This optimises:

如果对象的大小大于某个固定值(. net 1中的85000字节),那么CLR将其放入大型对象堆中。这个优化:

  1. Object allocation (small objects are not mixed with large objects)
  2. 对象分配(小对象不与大对象混合)
  3. Garbage collection (LOH collected only on full GC)
  4. 垃圾收集(仅在完整GC上收集LOH)
  5. Memory defragmentation (LOH is never rarely compacted)
  6. 内存碎片整理(LOH很少被压缩)

#3


7  

The essential difference of Small Object Heap (SOH) and Large Object Heap (LOH) is, memory in SOH gets compacted when collected, while LOH not, as this article illustrates. Compacting large objects costs a lot. Similar with the examples in the article, say moving a byte in memory needs 2 cycles, then compacting a 8MB object in a 2GHz computer needs 8ms, which is a large cost. Considering large objects (arrays in most cases) are quite common in practice, I suppose that's the reason why Microsoft pins large objects in the memory and proposes LOH.

小对象堆(SOH)和大对象堆(LOH)的本质区别是,在收集时SOH中的内存会被压缩,而LOH则不会,如本文所示。压缩大型对象的成本很高。类似于本文中的示例,比如在内存中移动一个字节需要两个周期,然后在2GHz计算机中压缩一个8MB的对象需要8ms,这是一个很大的成本。考虑到大型对象(大多数情况下是数组)在实践中非常常见,我认为这就是为什么Microsoft将大型对象固定在内存中并建议LOH。

BTW, according to this post, LOH usually doesn't generate memory fragment problems.

顺便说一句,根据这篇文章,LOH通常不会产生内存碎片问题。

#4


1  

The principal is that it unlikely (and quite possibly bad design) that a process would create lots of short lived large objects so the CLR allocates large objects to a separate heap on which it runs GC on a different schedule to the regular heap. http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

其主要原因是,一个进程不太可能(而且很可能是糟糕的设计)创建大量的短期大对象,因此CLR将大对象分配到一个单独的堆中,在这个堆上,它以不同的时间表运行GC。http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

#5


0  

I am not an expert on the CLR, but I would imagine that having a dedicated heap for large objects can prevent unnecessary GC sweeps of the existing generational heaps. Allocating a large object requires a significant amount of contiguous free memory. In order to provide that from the scattered "holes" in the generational heaps, you'd need frequent compactions (which are only done with GC cycles).

我不是CLR方面的专家,但是我可以想象,为大型对象创建一个专用堆可以防止不必要的GC清除现有的分代堆。分配一个大对象需要大量连续的空闲内存。为了从分代堆中分散的“漏洞”中提供这些信息,您将需要频繁的压缩(这只在GC周期中完成)。

#1


166  

A garbage collection doesn't just get rid of unreferenced objects, it also compacts the heap. That's a very important optimization. It doesn't just make memory usage more efficient (no unused holes), it makes the CPU cache much more efficient. The cache is a really big deal on modern processors, they are an easy order of magnitude faster than the memory bus.

垃圾收集不仅可以删除未引用的对象,还可以压缩堆。这是一个非常重要的优化。它不仅提高了内存使用效率(没有未使用的漏洞),而且使CPU缓存更加高效。缓存对于现代处理器来说是非常重要的,它们比内存总线要快得多。

Compacting is done simply by copying bytes. That however takes time. The larger the object, the more likely that the cost of copying it outweighs the possible CPU cache usage improvements.

压缩只需复制字节即可。,但是需要时间。对象越大,复制它的成本就越有可能超过可能的CPU缓存使用改进。

So they ran a bunch of benchmarks to determine the break-even point. And arrived at 85,000 bytes as the cutoff point where copying no longer improves perf. With a special exception for arrays of double, they are considered 'large' when the array has more than 1000 elements. That's another optimization for 32-bit code, the large object heap allocator has the special property that it allocates memory at addresses that are aligned to 8, unlike the regular generational allocator that only allocates aligned to 4. That alignment is a big deal for double, reading or writing a mis-aligned double is very expensive. Oddly the sparse Microsoft info never mention arrays of long, not sure what's up with that.

他们运行了一系列基准来确定盈亏平衡点。并且达到了85,000字节的截断点,复制不再提高性能。除了double数组的特殊例外,当数组中有超过1000个元素时,它们被认为是“大”的。这是32位代码的另一种优化,大型对象堆分配器具有特殊的属性,它将内存分配到与8对齐的地址,不像通常的代分配器,它只分配到4。双对齐对双对齐很重要,读或写双对齐不正确的双对齐非常昂贵。奇怪的是,稀疏的微软信息从来没有提到过长数组,不知道这是怎么回事。

Fwiw, there's lots of programmer angst about the large object heap not getting compacted. This invariably gets triggered when they write programs that consume more than half of the entire available address space. Followed by using a tool like a memory profiler to find out why the program bombed even though there was still lots of unused virtual memory available. Such a tool shows the holes in the LOH, unused chunks of memory where previously a large object lived but got garbage collected. Such is the inevitable price of the LOH, the hole can only be re-used by an allocation for an object that's equal or smaller in size. The real problem is assuming that a program should be allowed to consume all virtual memory at any time.

Fwiw,有很多程序员对大型对象堆没有被压缩感到焦虑。当他们编写的程序占用了整个可用地址空间的一半以上时,这种情况就会被触发。然后使用像内存分析器这样的工具来找出为什么程序会失败,即使仍然有许多未使用的虚拟内存可用。这样一个工具显示了LOH中的漏洞,这是一种未使用的内存块,以前大型对象在这里居住,但会收集垃圾。这就是LOH的必然价格,这个洞只能被分配给大小相等或更小的对象。真正的问题是假设一个程序应该被允许在任何时候使用所有的虚拟内存。

A problem that otherwise disappears completely by just running the code on a 64-bit operating system. A 64-bit process has 8 terabytes of virtual memory address space available, 3 orders of magnitude more than a 32-bit process. You just can't run out of holes.

在64位操作系统上运行代码就可以完全消除这个问题。64位进程有8 tb的虚拟内存地址空间可用,比32位进程多3个数量级。你就是不能没有洞。

Long story short, the LOH makes code run more efficient. At the cost of using available virtual memory address space less efficient.

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


UPDATE, .NET 4.5.1 now supports compacting the LOH, GCSettings.LargeObjectHeapCompactionMode property. Beware the consequences please.

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

#2


8  

If the object's size is greater than some pinned value (85000 bytes in .NET 1), then CLR puts it in Large Object Heap. This optimises:

如果对象的大小大于某个固定值(. net 1中的85000字节),那么CLR将其放入大型对象堆中。这个优化:

  1. Object allocation (small objects are not mixed with large objects)
  2. 对象分配(小对象不与大对象混合)
  3. Garbage collection (LOH collected only on full GC)
  4. 垃圾收集(仅在完整GC上收集LOH)
  5. Memory defragmentation (LOH is never rarely compacted)
  6. 内存碎片整理(LOH很少被压缩)

#3


7  

The essential difference of Small Object Heap (SOH) and Large Object Heap (LOH) is, memory in SOH gets compacted when collected, while LOH not, as this article illustrates. Compacting large objects costs a lot. Similar with the examples in the article, say moving a byte in memory needs 2 cycles, then compacting a 8MB object in a 2GHz computer needs 8ms, which is a large cost. Considering large objects (arrays in most cases) are quite common in practice, I suppose that's the reason why Microsoft pins large objects in the memory and proposes LOH.

小对象堆(SOH)和大对象堆(LOH)的本质区别是,在收集时SOH中的内存会被压缩,而LOH则不会,如本文所示。压缩大型对象的成本很高。类似于本文中的示例,比如在内存中移动一个字节需要两个周期,然后在2GHz计算机中压缩一个8MB的对象需要8ms,这是一个很大的成本。考虑到大型对象(大多数情况下是数组)在实践中非常常见,我认为这就是为什么Microsoft将大型对象固定在内存中并建议LOH。

BTW, according to this post, LOH usually doesn't generate memory fragment problems.

顺便说一句,根据这篇文章,LOH通常不会产生内存碎片问题。

#4


1  

The principal is that it unlikely (and quite possibly bad design) that a process would create lots of short lived large objects so the CLR allocates large objects to a separate heap on which it runs GC on a different schedule to the regular heap. http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

其主要原因是,一个进程不太可能(而且很可能是糟糕的设计)创建大量的短期大对象,因此CLR将大对象分配到一个单独的堆中,在这个堆上,它以不同的时间表运行GC。http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

#5


0  

I am not an expert on the CLR, but I would imagine that having a dedicated heap for large objects can prevent unnecessary GC sweeps of the existing generational heaps. Allocating a large object requires a significant amount of contiguous free memory. In order to provide that from the scattered "holes" in the generational heaps, you'd need frequent compactions (which are only done with GC cycles).

我不是CLR方面的专家,但是我可以想象,为大型对象创建一个专用堆可以防止不必要的GC清除现有的分代堆。分配一个大对象需要大量连续的空闲内存。为了从分代堆中分散的“漏洞”中提供这些信息,您将需要频繁的压缩(这只在GC周期中完成)。