强制对数组进行垃圾回收,C#

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1104352/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 08:21:03  来源:igfitidea点击:

Force garbage collection of arrays, C#

c#arraysgarbage-collectionxnaout-of-memory

提问by John Doe

I have a problem where a couple 3 dimensional arrays allocate a huge amount of memory and the program sometimes needs to replace them with bigger/smaller ones and throws an OutOfMemoryException.

我有一个问题,几个 3 维数组分配了大量内存,程序有时需要用更大/更小的数组替换它们并抛出 OutOfMemoryException。

Example: there are 5 allocated 96MB arrays (200x200x200, 12 bytes of data in each entry) and the program needs to replace them with 210x210x210 (111MB). It does it in a manner similar to this:

示例:有 5 个分配的 96MB 数组(200x200x200,每个条目中有 12 字节的数据),程序需要将它们替换为 210x210x210(111MB)。它以类似于此的方式执行此操作:

array1 = new Vector3[210,210,210];

Where array1-array5 are the same fields used previously. This should set the old arrays as candidates for garbage collection but seemingly the GC does not act quickly enough and leaves the old arrays allocated before allocating the new ones - which causes the OOM - whereas if they where freed before the new allocations the space should be enough.

其中 array1-array5 是以前使用的相同字段。这应该将旧数组设置为垃圾收集的候选对象,但似乎 GC 的动作不够快,并在分配新数组之前留下分配的旧数组 - 这会导致 OOM - 而如果它们在新分配之前被释放,则空间应该是足够的。

What I'm looking for is a way to do something like this:

我正在寻找的是一种做这样的事情的方法:

GC.Collect(array1) // this would set the reference to null and free the memory
array1 = new Vector3[210,210,210];

I'm not sure if a full garbage collecion would be a good idea since that code may (in some situations) need to be executed fairly often.

我不确定完整的垃圾收集是否是一个好主意,因为该代码可能(在某些情况下)需要经常执行。

Is there a proper way of doing this?

有没有正确的方法来做到这一点?

采纳答案by Chansik Im

This is not an exact answer to the original question, "how to force GC', yet, I think it will help you to reexamine your issue.

这不是对原始问题“如何强制 GC”的准确答案,但是,我认为它会帮助您重新审视您的问题。

After seeing your comment,

看到你的评论后,

  • Putting the GC.Collect(); does seem to help, altought it still does not solve the problem completely - for some reason the program still crashes when about 1.3GB are allocated (I'm using System.GC.GetTotalMemory( false ); to find the real amount allocated).
  • 放置 GC.Collect(); 似乎有帮助,尽管它仍然不能完全解决问题 - 由于某种原因,当分配了大约 1.3GB 时程序仍然崩溃(我正在使用 System.GC.GetTotalMemory(false); 来查找分配的实际数量)。

I will suspect you may have memory fragmentation. If the object is large (85000 bytes under .net 2.0 CLR if I remember correctly, I do not know whether it has been changed or not), the object will be allocated in a special heap, Large Object Heap (LOH). GC does reclaim the memory being used by unreachable objects in LOH, yet, it does not perform compaction,in LOH as it does to other heaps (gen0, gen1, and gen2), due to performance.

我会怀疑你可能有内存碎片。如果对象很大(如果我没记错的话,.net 2.0 CLR 下是 85000 字节,不知道有没有改变),对象会被分配到一个特殊的堆中,Large Object Heap (LOH)。GC 确实回收了 LOH 中无法访问的对象正在使用的内存,但由于性能原因,它不会像LOH 中的其他堆(gen0、gen1 和 gen2)那样执行压缩

If you do frequently allocate and deallocate large objects, it will make LOH fragmented and even though you have more free memory in total than what you need, you may not have a contiguous memory space anymore, hence, will get OutOfMemory exception.

如果你经常分配和释放大对象,它会使 LOH 碎片化,即使你有比你需要的更多的可用内存,你可能不再有连续的内存空间,因此,会得到 OutOfMemory 异常。

I can think two workarounds at this moment.

我现在可以想到两种解决方法。

  1. Move to 64-bit machine/OS and take advantage of it :) (Easiest, but possibly hardest as well depending on your resource constraints)
  2. If you cannot do #1, then try to allocate a huge chuck of memory first and use them (it may require to write some helper class to manipulate a smaller array, which in fact resides in a larger array) to avoid fragmentation. This may help a little bit, yet, it may not completely solve the issue and you may have to deal with the complexity.
  1. 转移到 64 位机器/操作系统并利用它:)(最简单,但也可能最难,具体取决于您的资源限制)
  2. 如果你不能做到#1,那么首先尝试分配一大块内存并使用它们(可能需要编写一些帮助类来操作较小的数组,实际上它驻留在较大的数组中)以避免碎片。这可能会有所帮助,但是,它可能无法完全解决问题,您可能必须处理复杂性。

回答by Mitch Wheat

Forcing a Garbage Collection is not always a good idea (it can actually promote the lifetimes of objects in some situations). If you have to, you would use:

强制垃圾回收并不总是一个好主意(它实际上可以在某些情况下促进对象的生命周期)。如果必须,您可以使用:

array1 = null;
GC.Collect();
array1 = new Vector3[210,210,210];

回答by Joseph

They might not be getting collected because they're being referenced somewhere you're not expecting.

它们可能不会被收集,因为它们在您意想不到的地方被引用。

As a test, try changing your references to WeakReferencesinstead and see if that resolves your OOM problem. If it doesn't then you're referencing them somewhere else.

作为测试,请尝试更改对WeakReferences 的引用,看看是否能解决您的 OOM 问题。如果不是,那么您将在其他地方引用它们。

回答by Mike Dinescu

If I had to speculate you problem is not really that you are going from Vector3[200,200,200] to a Vector3[210,210,210] but that most likely you have similar previous steps before this one:

如果我不得不推测你的问题并不是你真的从 Vector3[200,200,200] 到 Vector3[210,210,210] 但很可能你在这之前有类似的先前步骤:

 i.e.   
    // first you have
    Vector3[10,10,10];
    // then
    Vector3[20,20,20];
    // then maybe
    Vector3[30,30,30];
    //  .. and so on ..
    //  ...
    // then
    Vector3[200,200,200];
    // and eventually you try
    Vector3[210,210,210] // and you get an OutOfMemoryException..

If that is true, I would suggest a better allocation strategy. Try over allocating - maybe doubling the size every time as opposed to always allocating just the space that you need. Especially if these arrays are ever used by objects that need to pin the buffers (i.e. if that have ties to native code)

如果这是真的,我会建议一个更好的分配策略。尝试过度分配 - 也许每次都将大小加倍,而不是总是只分配您需要的空间。特别是如果这些数组曾经被需要固定缓冲区的对象使用(即如果与本机代码有联系)

So, instead of the above, have something like this:

所以,而不是上面的,有这样的事情:

 // first start with an arbitrary size
 Vector3[64,64,64];
 // then double that
 Vector3[128,128,128];
 // and then.. so in thee steps you go to where otherwise 
 // it would have taken you 20..
 Vector3[256,256,256];

回答by clemahieu

An OutOfMemory exception internally triggers a GC cycle automatically once and attempts the allocation again before actually throwing the exception to your code. The only way you could be having OutOfMemory exceptions is if you're holding references to too much memory. Clear the references as soon as you can by assigning them null.

OutOfMemory 异常会在内部自动触发一次 GC 循环,并在实际将异常抛出到您的代码之前再次尝试分配。您可能遇到 OutOfMemory 异常的唯一方法是您持有对太多内存的引用。通过将它们分配为空来尽快清除引用。

回答by Chansik Im

Isn't this just large object heap fragmentation? Objects > 85,000 bytes are allocated on the large object heap. The GC frees up space in this heap but never compacts the remaining objects. This can result in insufficent contiguous memory to successfully allocate a large object.

这不就是大对象堆碎片化吗?在大对象堆上分配了大于 85,000 字节的对象。GC 会释放该堆中的空间,但不会压缩剩余的对象。这可能会导致连续内存不足,无法成功分配大对象。

Alan.

艾伦。

回答by thecoop

Part of the problem may be that you're allocating a multidimensional array, which is represented as a single contiguous block of memory on the large object heap (more details here). This can block other allocations as there isn't a free contiguous block to use, even if there is still some free space somewhere, hence the OOM.

部分问题可能是您正在分配一个多维数组,该数组表示为大对象堆上的单个连续内存块(更多详细信息请点击此处)。这可能会阻止其他分配,因为没有可用的连续块可供使用,即使某处仍有一些可用空间,因此 OOM。

Try allocating it as a jagged array - Vector3[210][210][210] - which spreads the arrays around memory rather than as a single block, and see if that improves matters

尝试将其分配为锯齿状数组 - Vector3[210][210][210] - 将数组分布在内存周围而不是作为单个块,看看是否能改善问题

回答by plinth

I understand what you're trying to do and pushing for immediate garbage collection is probably not the right approach (since the GC is subtle in its ways and quick to anger).

我理解您正在尝试做的事情,并且推动立即进行垃圾收集可能不是正确的方法(因为 GC 的方式很微妙,而且很容易生气)。

That said, if you wantthat functionality, why not create it?

也就是说,如果您想要该功能,为什么不创建它呢?

public static void Collect(ref object o)
{
    o = null;
    GC.Collect();
}

回答by Pop Catalin

Seems you've run into LOH (Large object heap) fragmentation issue.

似乎您遇到了 LOH(大对象堆)碎片问题。

Large Object Heap

大对象堆

CLR Inside Out Large Object Heap Uncovered

发现 CLR 大对象堆

You can check to see if you're having loh fragmentation issues using SOS

您可以使用 SOS 检查是否有 loh 碎片问题

Check this questionfor an example of how to use SOS to inspect the loh.

检查此问题以获取有关如何使用 SOS 检查 loh 的示例。

回答by mfawzymkh

John, Creating objects > 85000 bytes will make the object end up in the large object heap. The large object heap is never compacted, instead the free space is reused again. This means that if you are allocating larger arrays every time, you can end up in situations where LOH is fragmented, hence the OOM.

John,创建对象 > 85000 字节将使对象最终进入大对象堆。大对象堆永远不会被压缩,而是再次重用空闲空间。这意味着如果您每次都分配更大的数组,则最终可能会出现 LOH 碎片化的情况,从而导致 OOM。

you can verify this is the case by breaking with the debugger at the point of OOM and getting a dump, submitting this dump to MS through a connect bug (http://connect.microsoft.com) would be a great start.

您可以通过在 OOM 点中断调试器并获取转储来验证这种情况,通过连接错误 ( http://connect.microsoft.com)将此转储提交给 MS将是一个很好的开始。

What I can assure you is that the GC will do the right thing trying to satisfy you allocation request, this includes kicking off a GC to clean the old garbage to satisfy the new allocation requests.

我可以向你保证的是,GC 会做正确的事情来满足你的分配请求,这包括启动 GC 来清理旧垃圾以满足新的分配请求。

I don't know what is the policy of sharing out memory dumps on Stackoverflow, but I would be happy to take a look to understand your problem more.

我不知道在 Stackoverflow 上共享内存转储的政策是什么,但我很乐意看一看以更多地了解您的问题。