Running into a prickly problem with our web app here. (Asp.net 2.0 Win server 2008)
我们的web应用程序遇到了一个棘手的问题。(Asp.net 2.0 Win server 2008)
Our memory usage for the website, grows and grows even though I would expect it to remain at a fairly static level. (We have a small amount of data that gets stored in state).
我们对网站的内存使用量在不断增长,尽管我认为它将保持在一个相当静态的水平。(我们有少量以状态存储的数据)。
Wanting to find out what the problem is, I've run a System.GC.Collect(); a few times, taken a memory dump and then loaded this memory dump into WinDbg.
为了找出问题所在,我运行了一个System.GC.Collect();有几次,取一个内存转储,然后将这个内存转储加载到WinDbg中。
When I do a DumpHeap -Stat I get an inordinately large number on particular type hanging around in memory.
当我做一个哑铃-Stat时,我得到一个非常大的数字,是关于在记忆中徘徊的特定类型的。
0000064280580b40 713471 79908752 PaymentOption
713471 3 0000064280580 79908752 PaymentOption
so, doing a DumpHeap -MT for this type, I get a stack of object references. Picking a random number of these, I do a !gcroot and the command comes back reporting that no references are held to it.
所以,为这种类型做一个DumpHeap -MT,我得到一堆对象引用。随机选择其中的一个,我做一个!gcroot,命令返回,报告没有引用它。
To me, this is exactly when the GC should collect these items, but for some reason they have been left outstanding.
对我来说,这正是GC应该收集这些项目的时候,但是由于某些原因,它们仍然是未完成的。
Can anybody offer an explanation as to what might be happening?
有人能解释一下可能发生的事情吗?
7 个解决方案
#1
4
You could try using sosex.dll in Windbg, which is an extension written to help with .NET debugging. There is a command named !refs which is similar to !gcroot, in that it will show you all the objects referencing an object, plus it will show all the objects that it too is referencing.
你可以尝试使用性爱。在Windbg中的dll,这是一个用来帮助。net调试的扩展。有一个命令名为!refs,它类似于gcroot,它将显示所有引用一个对象的对象,并且它将显示它所引用的所有对象。
In the example on the author's website, !refs is used against an object and the output looks like this:
在作者网站上的示例中,引用用于对象,输出如下:
0:000> !refs 0000000080000db8
Objects referenced by 0000000080000db8 (System.Threading.Mutex):
0000000080000ef0 32 Microsoft.Win32.SafeHandles.SafeWaitHandle
Objects referencing 0000000080000db8 (System.Threading.Mutex):
0000000080000e08 72 System.Threading.Mutex+<>c__DisplayClass3
0000000080000e50 64 System.Runtime.CompilerServices.RuntimeHelpers+CleanupCode
#2
2
Few things:
一些事情:
- GC.Collect won't help you do any debugging. The garbage collector is already being called: if any objects were available for collection it would have happened already.
- GC。Collect不会帮助您进行任何调试。垃圾收集器已经被调用:如果有任何对象可供收集,那么它已经发生了。
- Idle memory on a server is wasted memory. Are you sure memory is being 'leaked', or is it just that the framework is deciding it can keep more things in memroy or keep more memory around for faster access? In this case I suspect you are leaking memory, but it's something to double check for.
- 服务器上的空闲内存是浪费的内存。你确定内存是被“泄露”的,还是仅仅是框架决定它可以在内存中保留更多的东西,或者保留更多的内存以便更快地访问?在这种情况下,我怀疑您正在泄漏内存,但是需要仔细检查。
- It sounds like something you don't expect is keeping a reference to PaymentOption objects. Perhaps a static collection somewhere? Or separate thread?
- 这听起来像是你不期望的东西,它保留了对PaymentOption对象的引用。也许是某个地方的静态集合?或单独的线程吗?
#3
2
Does PaymentObject implement a finalizer by any chance? Does it call a STA COM object?
PaymentObject是否有机会实现终结器?它调用STA COM对象吗?
I'd be curious to see the output of !finalizequeue to see if the count of objects that are showing up on the heap are roughly the amount of any that might waiting to be finalized. Output should probably look something like this:
我很想看看!finalizequeue的输出,看看在堆上显示的对象的数量是否大约是任何可能等待最终确定的对象的数量。输出应该是这样的:
generation 0 has 57 finalizable objects (0409b5cc->0409b6b0)
generation 1 has 55 finalizable objects (0409b4f0->0409b5cc)
generation 2 has 0 finalizable objects (0409b4f0->0409b4f0)
Ready for finalization 0 objects (0409b6b0->0409b6b0)
If the number of Ready for finalization objects continues to grow, and your certain garbage collections are occuring (confirm via perfmon counters), then it might be a blocked finalizer thread. You might need to take several snapshots over the lifetime of the process (before a recycle) to confirm. I usually rely on the magic number of three, as long as the site is under some sort of load.
如果准备终结对象的数量继续增加,并且您的某些垃圾收集正在发生(通过perfmon计数器进行确认),那么它可能是一个阻塞的终结器线程。您可能需要在流程的生命周期中(在循环之前)拍摄几个快照来确认。我通常依靠神奇的数字3,只要网站在某种负载下。
A bug in a finalizer can block the finalizer thread and prevent the objects from ever being collected.
终结器中的一个bug可以阻塞终结器线程并阻止对象被收集。
If the PaymentOption object calls a legacy STA COM object, then this article ASP.NET Hang and OutOfMemory exceptions caused by STA components might point in the right direction.
如果PaymentOption对象调用遗留STA COM对象,那么本文将使用ASP。STA组件引起的NET Hang out和OutOfMemory异常可能指向正确的方向。
#4
1
Not without more info on your application. But we ran into some nasty memory problems a long time ago. Do you use ASP.NET caching? As Raymond Chen likes to say, "poor caching strategy is indisitinguishable from a memory leak."
没有关于你的应用程序的更多信息。但是很久以前我们遇到了一些严重的记忆问题。你使用ASP。网络缓存?正如Raymond Chen喜欢说的,“糟糕的缓存策略与内存泄漏是不可分割的。”
Check out another tool - CLRProfiler.exe - it will help you traverse object reference trees to see where your objects are rooted. This is also good: link text
查看另一个工具——CLRProfiler。exe——它将帮助您遍历对象引用树,以查看对象的根在哪里。这也很好:链接文本
You've heard this before - if you have to GC.Collect, something is wrong.
您以前听说过这个——如果您必须使用GC的话。收集,是错误的。
#5
1
Is the PaymentOption object created in an asynchronous process, by any chance? I remember something about, if you don't call EndInvoke, you can get problems like this.
PaymentOption对象是否在异步进程中创建?我记得,如果你不调用EndInvoke,你会遇到这样的问题。
#6
1
I've been investigating the same issue myself and was asking why objects that had no references were not being collected.
我自己也在研究同样的问题,并问为什么没有引用的对象没有被收集。
Objects larger than 85,000 bytes are stored on the Large Object Heap, from which memory is freed up less frequently.
大于85,000字节的对象存储在大型对象堆上,从该对象堆中释放内存的频率降低。
http://msdn.microsoft.com/en-us/magazine/cc534993.aspx
http://msdn.microsoft.com/en-us/magazine/cc534993.aspx
A single PaymentOption may not be that big, but are they contained within collections, or are they based on something like a DataSet? You should pick on few instances of the PaymentOption / collection / DataSet and then use the sos !objsize command to see big they are.
单个PaymentOption可能没有那么大,但它们是包含在集合中,还是基于数据集?您应该选择PaymentOption / collection / DataSet的几个实例,然后使用sos !objsize命令查看它们的大小。
Unfortunately this doesn't really answer the question. I like to think I can trust the .net framework to take care of releasing unused memory whenever it needs to. However I see a lot of memory being used by the worker process running the app I am looking at, even when memory looks quite tight on the server.
不幸的是,这并不能真正回答这个问题。我倾向于相信。net框架可以在需要的时候释放未使用的内存。然而,我看到运行我正在查看的应用程序的worker进程正在使用大量内存,即使服务器上的内存看起来非常紧张。
#7
0
FYI, SOS in .NET 4 supports a few new commands that might be of assistance, namely !gcwhere
(locate the generation of an objection; sosex's gcgen) and !findroots
(does what it says on the tin; sosex's !refs)
顺便提一下,在。net 4中的SOS支持了一些可能有帮助的新命令,即!sosex的gcgen)和!findroots(做它在tin上说的;参sosex的!)
Both are documented on the SOS documentation and mentioned on Tess Ferrandez's blog.
两人都被记录在SOS文档上,并在Tess Ferrandez的博客上提到。
#1
4
You could try using sosex.dll in Windbg, which is an extension written to help with .NET debugging. There is a command named !refs which is similar to !gcroot, in that it will show you all the objects referencing an object, plus it will show all the objects that it too is referencing.
你可以尝试使用性爱。在Windbg中的dll,这是一个用来帮助。net调试的扩展。有一个命令名为!refs,它类似于gcroot,它将显示所有引用一个对象的对象,并且它将显示它所引用的所有对象。
In the example on the author's website, !refs is used against an object and the output looks like this:
在作者网站上的示例中,引用用于对象,输出如下:
0:000> !refs 0000000080000db8
Objects referenced by 0000000080000db8 (System.Threading.Mutex):
0000000080000ef0 32 Microsoft.Win32.SafeHandles.SafeWaitHandle
Objects referencing 0000000080000db8 (System.Threading.Mutex):
0000000080000e08 72 System.Threading.Mutex+<>c__DisplayClass3
0000000080000e50 64 System.Runtime.CompilerServices.RuntimeHelpers+CleanupCode
#2
2
Few things:
一些事情:
- GC.Collect won't help you do any debugging. The garbage collector is already being called: if any objects were available for collection it would have happened already.
- GC。Collect不会帮助您进行任何调试。垃圾收集器已经被调用:如果有任何对象可供收集,那么它已经发生了。
- Idle memory on a server is wasted memory. Are you sure memory is being 'leaked', or is it just that the framework is deciding it can keep more things in memroy or keep more memory around for faster access? In this case I suspect you are leaking memory, but it's something to double check for.
- 服务器上的空闲内存是浪费的内存。你确定内存是被“泄露”的,还是仅仅是框架决定它可以在内存中保留更多的东西,或者保留更多的内存以便更快地访问?在这种情况下,我怀疑您正在泄漏内存,但是需要仔细检查。
- It sounds like something you don't expect is keeping a reference to PaymentOption objects. Perhaps a static collection somewhere? Or separate thread?
- 这听起来像是你不期望的东西,它保留了对PaymentOption对象的引用。也许是某个地方的静态集合?或单独的线程吗?
#3
2
Does PaymentObject implement a finalizer by any chance? Does it call a STA COM object?
PaymentObject是否有机会实现终结器?它调用STA COM对象吗?
I'd be curious to see the output of !finalizequeue to see if the count of objects that are showing up on the heap are roughly the amount of any that might waiting to be finalized. Output should probably look something like this:
我很想看看!finalizequeue的输出,看看在堆上显示的对象的数量是否大约是任何可能等待最终确定的对象的数量。输出应该是这样的:
generation 0 has 57 finalizable objects (0409b5cc->0409b6b0)
generation 1 has 55 finalizable objects (0409b4f0->0409b5cc)
generation 2 has 0 finalizable objects (0409b4f0->0409b4f0)
Ready for finalization 0 objects (0409b6b0->0409b6b0)
If the number of Ready for finalization objects continues to grow, and your certain garbage collections are occuring (confirm via perfmon counters), then it might be a blocked finalizer thread. You might need to take several snapshots over the lifetime of the process (before a recycle) to confirm. I usually rely on the magic number of three, as long as the site is under some sort of load.
如果准备终结对象的数量继续增加,并且您的某些垃圾收集正在发生(通过perfmon计数器进行确认),那么它可能是一个阻塞的终结器线程。您可能需要在流程的生命周期中(在循环之前)拍摄几个快照来确认。我通常依靠神奇的数字3,只要网站在某种负载下。
A bug in a finalizer can block the finalizer thread and prevent the objects from ever being collected.
终结器中的一个bug可以阻塞终结器线程并阻止对象被收集。
If the PaymentOption object calls a legacy STA COM object, then this article ASP.NET Hang and OutOfMemory exceptions caused by STA components might point in the right direction.
如果PaymentOption对象调用遗留STA COM对象,那么本文将使用ASP。STA组件引起的NET Hang out和OutOfMemory异常可能指向正确的方向。
#4
1
Not without more info on your application. But we ran into some nasty memory problems a long time ago. Do you use ASP.NET caching? As Raymond Chen likes to say, "poor caching strategy is indisitinguishable from a memory leak."
没有关于你的应用程序的更多信息。但是很久以前我们遇到了一些严重的记忆问题。你使用ASP。网络缓存?正如Raymond Chen喜欢说的,“糟糕的缓存策略与内存泄漏是不可分割的。”
Check out another tool - CLRProfiler.exe - it will help you traverse object reference trees to see where your objects are rooted. This is also good: link text
查看另一个工具——CLRProfiler。exe——它将帮助您遍历对象引用树,以查看对象的根在哪里。这也很好:链接文本
You've heard this before - if you have to GC.Collect, something is wrong.
您以前听说过这个——如果您必须使用GC的话。收集,是错误的。
#5
1
Is the PaymentOption object created in an asynchronous process, by any chance? I remember something about, if you don't call EndInvoke, you can get problems like this.
PaymentOption对象是否在异步进程中创建?我记得,如果你不调用EndInvoke,你会遇到这样的问题。
#6
1
I've been investigating the same issue myself and was asking why objects that had no references were not being collected.
我自己也在研究同样的问题,并问为什么没有引用的对象没有被收集。
Objects larger than 85,000 bytes are stored on the Large Object Heap, from which memory is freed up less frequently.
大于85,000字节的对象存储在大型对象堆上,从该对象堆中释放内存的频率降低。
http://msdn.microsoft.com/en-us/magazine/cc534993.aspx
http://msdn.microsoft.com/en-us/magazine/cc534993.aspx
A single PaymentOption may not be that big, but are they contained within collections, or are they based on something like a DataSet? You should pick on few instances of the PaymentOption / collection / DataSet and then use the sos !objsize command to see big they are.
单个PaymentOption可能没有那么大,但它们是包含在集合中,还是基于数据集?您应该选择PaymentOption / collection / DataSet的几个实例,然后使用sos !objsize命令查看它们的大小。
Unfortunately this doesn't really answer the question. I like to think I can trust the .net framework to take care of releasing unused memory whenever it needs to. However I see a lot of memory being used by the worker process running the app I am looking at, even when memory looks quite tight on the server.
不幸的是,这并不能真正回答这个问题。我倾向于相信。net框架可以在需要的时候释放未使用的内存。然而,我看到运行我正在查看的应用程序的worker进程正在使用大量内存,即使服务器上的内存看起来非常紧张。
#7
0
FYI, SOS in .NET 4 supports a few new commands that might be of assistance, namely !gcwhere
(locate the generation of an objection; sosex's gcgen) and !findroots
(does what it says on the tin; sosex's !refs)
顺便提一下,在。net 4中的SOS支持了一些可能有帮助的新命令,即!sosex的gcgen)和!findroots(做它在tin上说的;参sosex的!)
Both are documented on the SOS documentation and mentioned on Tess Ferrandez's blog.
两人都被记录在SOS文档上,并在Tess Ferrandez的博客上提到。