何时解决托管堆碎片问题
我正在阅读博客文章 作者:Josh Smith,他使用缓存机制来“减少托管堆碎片”。他的缓存减少了创建的短期对象的数量,但代价是执行速度稍慢。
在像 C# 这样的托管语言中,托管堆碎片问题有多大?如何诊断是否存在问题?在什么情况下您通常需要解决这个问题?
I was reading a blog entry by Josh Smith where he used a cache mechanism in order to "reduce managed heap fragmentation". His caching reduces the number of short-lived objects being created at the cost of slightly slower execution speed.
How much of a problem is managed heap fragmentation in a managed language like C#? How can you diagnose if it's an issue? In what situations would you typically need to address it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不太快。拥有短寿命的物体通常非常便宜。为了使缓存能够盈利,必须有(非常)多的候选者,并且他们应该活得足够长,以便能够传到下一代。
使用分析器。我不太确定这篇文章的作者是否这样做了。
据我所知这种情况很少见。 .NET 有一个压缩垃圾收集器,可以防止大多数形式的碎片。大对象堆有时会出现问题。
编辑:
当您浏览文章下面的评论时,您会发现有人测量了它并发现缓存比每次创建新的 eventargs 慢很多。
结论:在开始优化之前进行衡量。这不是一个好主意/例子。
Not too quickly. It is generally very cheap to have short lived objects. For a cache to be profitable there would have to be (very) many candiadates and they should live long enough to make it to the next generation.
With a Profiler. I'm not so sure the author of the article did that.
As far as I know it is rare. .NET has a compacting Garbage collector, that prevents most forms of fragmentation. There are issues with the Large Object Heap sometimes.
Edit:
When you go through the comments below the article you will find that someone measured it and found the cache to be a lot slower than creating new eventargs each time.
Conclusion: Measure before you start optimizing. This was not a good idea/example.
除非您每秒处理 10K 以上的小型短期对象,否则在具有合理 RAM 量的现代计算机上这根本不应该成为问题。
因此,首先您应该在所有合理的情况下运行代码,如果它足够快 - 不要担心。
如果您对速度不满意,您会发现代码有时会“阻塞”或者只是好奇,您可以监视各种 .NET 内存统计信息 (http://msdn.microsoft.com/en-us/library/x2tyfybc.aspx)位于性能监视器应用程序(作为 Windows 的一部分)。具体来说,您对 GC 中的时间百分比感兴趣。
redgate ANTS Profiler 也监控这些统计数据。
Unless you are dealing with 10K+ small short-lived objects per second, it should not be an issue at all on a modern computer with reasonable amount of RAM.
So first you should run the code in all reasonable scenarios and if it's fast enough - don't worry about it.
If you are not happy with the speed, you see that code sometime 'chokes' or a just curious, you can monitor various .NET Memory stats (http://msdn.microsoft.com/en-us/library/x2tyfybc.aspx) in Performance Monitor app (comes as part of Windows). Specifically you are interested in % Time in GC.
redgate ANTS profiler also monitors these stats.
托管堆碎片通常是由于对象固定造成的。当托管代码将对象指针传递给本机代码时,对象将被固定,并且该对象无法移动,因为引用已传递给本机代码。当有大量 I/O 活动时,这种情况很常见。就像上面提到的,它通常只发生在 LOH 中。
以下是 碎片的示例在 Gen0 堆中
Managed heap fragmentation is usually because of pinning of objects. Objects get pinned when managed code passes the object pointer to the native code and the object cannot be moved because the reference is passed to the native code. This is very common when there is a lot of I/O activities. Like mentioned above it usually happens only in LOH.
Here is an example of Fragmentation in Gen0 Heap
与这里给出的其他答案不同,我声明:是的,应该注意碎片!它不仅适用于托管堆,而且适用于
所有应用程序。由于 LOB 不会被压缩,因此随着时间的推移,一旦对象的大小和数量超过某个值(与可用的最大堆大小有关),它很可能会变得碎片化。如果确实如此,唯一安全的方法是限制立即保留的对这些对象的引用的数量。仅当池中的对象可以重用时,缓存(池)才会有帮助。有时,如果这些资源由不同长度 fe 的数组组成,它们可能不容易重用。所以池化在这里可能没有多大帮助。
如何检测呢?当 LOB 堆上有很大的压力时。怎么查出来是不是?同时使用.NET性能计数器“Collection Count Gen 0...2”。如果从 LOB 分配了太多大对象,则所有计数器都会以相同的方式演变。这意味着,基本上所有系列都是昂贵的第二代系列。在这种情况下,就应该采取一些措施。
对于较小的对象,我会让 GC 完成 Gen 0 集合中的所有工作,不用担心。
Unlike other answers given here I state: yes, one should take care of fragmentation! It does not only apply to managed heaps but to all apps handling (at least)
Since the LOB does not get compacted, it - over time - most probably will get fragmented as soon as the size and number of the objects exceed a certain value (which relates to the overall max heapsize available). If it does, the only safe way is to limit the number of instantly holded references to those objects. A cache (pool) would only help, if objects pooled can be reused. Sometimes, if these ressources are made of arrays of varying length f.e., they might not be reusable easily. So pooling may not help much here.
How to detect it? When ever there is large pressure on the LOB heap. How to find out it is? Use the .NET performance counter "Collection Count Gen 0...2" at the same time. If too many large objects are allocated from the LOB, all counters will evolve identically. Meaning, basically all collections are expensive generation 2 collections. In that case, there should something be done.
Regarding smaller objects, I would let the GC do all the work in Gen 0 collections and dont worry.