.net 集合内存优化 - 此方法有效吗?
就像几乎任何其他大型 .NET
应用程序一样,我当前的 C#
项目包含许多 .net
集合。
有时我从一开始就不知道集合(List/ObservableCollection/Dictionary/etc.)的大小是多少。
但很多时候我确实知道会发生什么。
我经常遇到 OutOfMemoryException
,并且我被告知发生这种情况不仅是因为进程大小限制,而且还因为碎片。
所以我的问题是 - 每次我知道集合的预期大小时设置集合的大小(使用构造函数中的容量参数)是否有助于我防止至少一些碎片问题?
此引用来自 msdn :
如果集合的大小可以是 估计,指定初始 容量消除了需要 执行多次调整大小 添加元素时的操作 列表。
但是,我仍然不想开始更改代码的大部分内容,以解决可能不是真正问题的问题。
它有帮助过你们解决内存不足的问题吗?
Just like almost any other big .NET
application, my current C#
project contains many .net
collections .
Sometimes I don't know, from the beginning, what the size of a Collection (List/ObservableCollection/Dictionary/etc.) is going to be.
But there are many times when I do know what it is going to be.
I often get an OutOfMemoryException
and I've been told it can happen not only because process size limits, but also because of fragmentation.
So my question is this - will setting collection's size (using the capacity argument in the constructor) every time I know its expected size help me prevent at least some of the fragmentation problems ?
This quote is from the msdn :
If the size of the collection can be
estimated, specifying the initial
capacity eliminates the need to
perform a number of resizing
operations while adding elements to
the List.
But still, I don't want to start changing big parts of my code for something that might not be the real problem.
Has it ever helped any of you to solve out of memory problems ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
指定初始大小很少会消除 OutOfMemory 问题 - 除非您的集合大小是数百万个对象,在这种情况下您确实不应该保留这样的集合。
调整集合的大小涉及定义一个具有新的附加大小的全新数组,然后复制内存。如果您已经接近内存不足,是的,这可能会导致内存不足,因为无法分配新数组。
但是,百分之九十九的应用都存在内存泄漏,而集合大小调整问题只是其中的一个症状。
Specifying an initial size will rarely if ever get rid of an OutOfMemory issue - unless your collection size is millions of object in which case you should really not keep such a collection.
Resizing a collection involves defining a completely new array with a new additional size and then copying the memory. If you are already close to out of memory, yes, this can cause an out of memory since the new array cannot be allocated.
However, 99 out of 100, you have a memory leak in your app and collection resizing issues is only a symptom of it.
如果您遇到 OOM,那么您可能对数据过于激进,但要回答这个问题:
是的,这可能会帮助一些 - 就好像它必须通过加倍来不断增长集合一样,它最终可能会为底层数组分配和复制两倍的内存(或者更准确地说,对于被丢弃的早期较小副本)。大多数中间数组都会被立即收集,但是当它们变大时,您将使用“大对象堆”,这更难压缩。
从正确的大小开始可以防止数组的所有中间副本。
但是,这还取决于数组中的内容。通常,对于类来说,每个对象中都有更多数据(加上引用等的开销) - 这意味着列表不一定是内存使用的最大罪魁祸首;您可能会消耗掉对象上的大部分内存。
请注意,x64 将允许更多的总体空间,但数组限制为 2GB - 如果每个引用的大小加倍,则数组的最大有效长度将减半。
就我个人而言,我会考虑将巨大的集合分解为较小的列表链;例如,锯齿状列表。
If you are hitting OOM, then you may be being overly aggressive with the data, but to answer the question:
Yes, this may help some - as if it has to keep growing the collections by doubling, it could end up allocating and copying twice as much memory for the underlying array (or more precicely, for the earlier smaller copies that are discarded). Most of these intermediate arrays will be collected promptly, but when they get big you are using the "large object heap", which is harder to compact.
Starting with the correct size prevents all the intermediate copies of the array.
However, it also depends what is in the array matters. Typically, for classes, there is more data in each object (plus overheads for references etc) - meaning the list is not necessarily the biggest culprit for memory use; you might be burning up most of the memory on objects.
Note that x64 will allow more overall space, but arrays are limited to 2GB - and if each reference doubles in size this halves the maximum effective length of the array.
Personally I would look at breaking the huge sets into smaller chains of lists; jagged lists, for example.
.NET 有一个兼容的垃圾收集器,因此您可能不会在正常的 .NET 堆上遇到碎片问题。但是,如果您使用大量非托管内存(例如通过 GDI+、COM 等),则可能会出现内存碎片。此外,大型对象堆没有被压缩,因此也可能会产生碎片。 IIRC 如果对象大于 80kb,则将其放入 LOH 中。因此,如果您有许多包含超过 20k 对象的集合,您可能会遇到碎片问题。
但是,与其猜测问题可能出在哪里,不如进一步缩小问题范围:何时收到 OutOfMemoryExceptions?当时应用程序使用了多少内存?使用 WinDbg 或内存分析器等工具,您应该能够找出 LOH 上有多少内存。
也就是说,如果您知道的话,提前设置 List 和其他数据结构的容量总是一个好主意。否则,每次添加项目并达到容量限制时,列表的容量都会增加一倍,这意味着大量不必要的分配和复制操作。
.NET has a compating garbage collector, so you probably won't run into fragmentation problems on the normal .NET heap. You can however get memory fragmentation if you're using lots of unmanaged memory (e.g. through GDI+, COM, etc.). Also, the large object heap isn't compacted, so that can get fragmented, too. IIRC an object is put into the LOH if it's bigger than 80kb. So if you have many collections that contain more than 20k objects, you might get fragmentation problems.
But instead of guessing where the problem might be, it might be better to narrow the problem down some more: When do you get the OutOfMemoryExceptions? How much memory is the application using at that time? Using a tool like WinDbg or memory profilers you should be able to find out how much of that memory is on the LOH.
That said, it's always a good idea to set the capacity of List and other data structures in advance if you know it. Otherwise, the List will double it's capacity everytime you add an item and hit the capacity limit which means lots of unnecessary allocation and copy operations.
为了解决这个问题,您必须了解基础知识并找出代码中的问题。
如果您有合理的估计,那么设置初始容量始终是一个好主意。如果您只有大概的猜测,请分配更多。
碎片只能发生在 LOH(超过 80 kB 的对象)上。为了防止这种情况,请尝试分配相同大小的块。矛盾的是,解决方案有时可能会分配比实际需要更多的内存。
In order to solve this, you have to understand the basics and pinpoint the problem in your code.
It is always a good idea to set the initial capacity, if you have a sensible estimate. If you only have an approximate guess, allocate more.
Fragmentation can only occur on the LOH (objects over 80 kB). To prevent it , try to allocate blocks of the same size. Paradoxically, the solution might be to sometimes allocate more memory than you actually need.
答案是,是的,预先定义集合的大小将提高性能和内存优化并减少碎片。请参阅我的答案,了解原因 - 如果我设置 .NET 集合的初始大小,然后添加一些超出此初始大小的项目,该集合如何确定下一次调整大小?
但是,如果不分析应用程序上的内存转储或内存分析,就不可能准确说出 OOM 的原因。因此,无法猜测此优化是否能解决问题。
The answer is that, yes pre-defining a size on collections will increase performance and memory optimization and reduce fragmentation. See my answer here to see why - If I set the initial size of a .NET collection and then add some items OVER this initial size, how does the collection determine the next resize?
However, without analyzing a memory dump or memory profiling on the app, it's impossible to say exactly what the cause of the OOM is. Thus, impossible to conjecture if this optimization will solve the problem.