Gen2 收集并不总是收集死对象? [英] Gen2 collection not always collecting dead objects?

查看:17
本文介绍了Gen2 收集并不总是收集死对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过监视过去几天全新 .NET 4.5 服务器应用程序的 CLR #Bytes in all Heaps 性能计数器,我可以注意到一种模式,使我认为 Gen2 集合不是总是收集死对象,但我无法理解到底发生了什么.

By monitoring the CLR #Bytes in all Heaps performance counter of a brand new .NET 4.5 server application over the last few days, I can notice a pattern that makes me think that Gen2 collection is not always collecting dead objects, but I am having trouble understanding what exactly is going on.

服务器应用程序正在 .NET Framework 4.5.1 中使用服务器 GC/后台运行.

这是一个作为 Windows 服务托管的控制台应用程序(在 Topshelf 框架的帮助下)

This is a console application hosted as a Windows Service (with the help of Topshelf framework)

服务器应用程序正在处理消息,目前吞吐量在某种程度上相当稳定.

The server application is processing messages, and the throughput is somehow pretty constant for now.

CLR #Bytes in all Heaps 的图表可以看出,内存从 18MB 开始,然后在大约 20-24 小时内增长到 35MB(在 20-30 Gen2 之间)在那个时间范围内收集),然后突然回落到 18MB 的标称值,然后在 20-24 小时内再次增长到 ~35MB 并回落到 18MB,依此类推(我可以看到模式重复应用程序现在运行的最后 6 天)......内存的增长不是线性的,增长 10MB 需要大约 5 小时,剩余的 10MB 大约需要 15-17 小时.

What I can see looking at the graph of CLR #Bytes in all Heaps is that the memory started arround 18MB then growing up to 35MB on approx 20-24 hours (with between 20-30 Gen2 collections during that time frame), and then all of a sudden dropping back to nominal value of 18MB, then growing again up to ~35MB over 20-24 hours and dropping back to 18MB, and so on (I can see the pattern repeating over the last 6 days the app is now running) ... The growing of memory is not linear, it takes approx 5 hours to grow by 10MB and then 15-17 hours for the remaining 10 MB or so.

事情是,我可以通过查看 #Gen0/#Gen1/#Gen2 集合 的 perfmon 计数器看到一堆 Gen2 集合在 20-24 小时内(可能大约在30) 并且它们都没有使内存回落到标称 18MB.然而,奇怪的是通过使用外部工具强制 GC(在我的例子中是 Perfview),然后我可以看到 #Induced GC 上升了 1(GC.Collect 被调用所以这是正常的) 并且内存立即恢复到标称 18MB.

Thing is that I can see by looking at perfmon counters for #Gen0/#Gen1/#Gen2 collections that a bunch of Gen2 collections are going on during the 20-24 hours period (maybe arround 30) and none of them makes the memory drop back to nominal 18MB. However, what is strange is by using an external tool to force a GC (Perfview in my case), then I can see #Induced GC going up by 1 (GC.Collect was called so this is normal) and immediately the memory is going back to nominal 18MB.

这让我想到要么#Gen2 集合的性能计数器不正确,并且只有一个 Gen2 集合在 20-22 小时左右后发生(meeehhh 我真的不这么认为)或者 Gen2 集合没有总是收集死对象(似乎更合理)......但在这种情况下,为什么通过 GC.Collect 强制 GC 会起作用,在生命周期内显式调用 GC.Collect 与自动触发的集合有什么区别?应用程序.

Which leads me into thinking that either the perfmon counter for #Gen2 collections is not right and only a single Gen2 collection happens after 20-22hours or so (meeehhh I really don't think so) or that the Gen2 collection does not always collect dead objects (seems more plausible) ... but in that case why would forcing a GC via GC.Collect do the trick, what would be the difference between explicitely calling into GC.Collect, v.s automatic triggered collections during the lifetime of the application.

我确信有一个很好的解释,但是从不同的文档来源我发现了关于 GC 的文档 - 太少了 :( - Gen2 集合在任何情况下都会收集死对象.所以也许文档不是最新的或我读错了......欢迎任何解释.谢谢!

I am sure there is a very good explanation but from the different source of documentation I have found about GC -too few :(- a Gen2 collection does collect dead objects in any case. So maybe docs are not up to date or I have misread ... Any explanation is welcome. Thanks !

请查看 4 天内 #Bytes in all heaps 图表的此屏幕截图

EDIT : Please see this screenshot of the #Bytes in all heaps graph over 4 days

(点击查看大图)

这比尝试在脑海中描绘事物更容易.你在图表上看到的是我上面所说的......内存增加了 20-24 小时(在那个时间范围内增加了 20-30 个 Gen2 集合),直到达到 ~35MB 然后突然下降.您会在图的末尾注意到,我通过外部工具触发的诱导 GC,立即将内存回落到标称值.

this is easier than trying to graph things in your head. What you can see on the graph is what I said above... memory increasing over 20-24hours (and 20-30 Gen2 collections during that time frame) until reaching ~35MB then dropping all of a sudden. You will note at the end of the graph, the induced GC I triggered via an external tool, immediately dropping back memory to nominal.

编辑 #2 : 我在代码中做了很多清理,主要是关于终结器.我有很多类都持有对一次性类型的引用,所以我必须在这些类型上实现 IDisposable.然而,我被一些文章误导,在任何情况下都用终结器实现了 Diposable 模式.在阅读了一些 MSDN 文档后,我开始理解只有当类型本身持有本机资源时才需要终结器(在这种情况下,可以使用 SafeHandle 避免这种情况).所以我从所有这些类型中删除了所有终结器.代码中还有一些其他修改,但主要是业务逻辑,与.NET 框架"无关.现在图表非常不同,这是一条大约 20MB 的平线,现在几天......正是我期望看到的!所以问题现在已经解决了,但是我仍然不知道问题是什么原因......似乎它可能与终结器有关但仍然没有解释我注意到的内容,即使我们没有调用 Dispose(真)-抑制终结器-,终结器线程应该在收集之间启动,而不是每 20-24 小时启动一次?!考虑到我们现在已经摆脱了这个问题,回到有问题"的版本并再次重现它需要时间.不过,我可能会尝试做一段时间并深入了解它.

EDIT #2 : I made a lot of cleaning in the code, mainly regarding finalizers. I had a lot of classes that were holding reference to disposable types, so I had to implement IDisposable on these types. However I was misguided by some articles into implementing the Diposable pattern with a Finalizer in any case. After reading some MSDN documentation I came to understand that a finalizer was only required when the type was holding native resources itself (and still in that case this could be avoided with SafeHandle). So I removed all finalizers from all these types. There were some other modications in the code, but mainly business logic, nothing ".NET framework" related. Now the graph is very different, this is a flat line arround 20MB for days now ... exactly what I was expecting to see ! So the problem is now fixed, however I still have no idea what was the problem due to ... It seems like it might have been related to finalizers but still does not explain what I was noticing, even if we weren't calling Dispose(true) -suppressing finalizer-, the finalizer thread is supposed to kick in between collection and not every 20-24 hours ?! Considering we have now moved away from the problem, it will take time to come back to the "buggy" version and reproduce it again. I may try to do it some time though and go to the bottom of it.

添加了 Gen2 集合图 (点击查看大图)

Added Gen2 collection graph (Click for larger view)

推荐答案

来自

http://msdn.microsoft.com/en-us/library/ee787088%28v=VS.110%29.aspx#workstation_and_server_garbage_collection

垃圾收集发生在以下情况之一时真的:

Conditions for a garbage collection

Garbage collection occurs when one of the following conditions is true:

  • 系统物理内存不足.

  • The system has low physical memory.

托管堆上已分配对象使用的内存超过了可接受的阈值.这个阈值是连续的随着流程的运行进行调整.

The memory that is used by allocated objects on the managed heap surpasses an acceptable threshold. This threshold is continuously adjusted as the process runs.

GC.Collect 方法被调用.几乎在所有情况下,您都不必调用此方法,因为垃圾收集器会运行不断.这种方法主要用于特殊情况和测试.

The GC.Collect method is called. In almost all cases, you do not have to call this method, because the garbage collector runs continuously. This method is primarily used for unique situations and testing.

看来您已达到第 2 个,而 35 是阈值.如果 35 太大,您应该能够将阈值配置为其他值.

It seems that you are hitting the 2nd one and 35 is the threshold. You should be able to configure the threshold to something else if 35 is to large.

gen2 集合没有任何特别之处会导致它们偏离这些规则.(参见 https://stackoverflow.com/a/8582251/215752)

There isn't anything special about gen2 collections that would cause them to deviate from these rules. (cf https://stackoverflow.com/a/8582251/215752)

这篇关于Gen2 收集并不总是收集死对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆