我正在运行一个为HTTP请求提供服务的C#应用程序。我最近注意到它占据了我预期的更多内存。我抓住了一些转储,在Windbg中弹出它们,发现大部分内存被标记为Free:
!dumpheap -stat
...
00007ffde4783630 681599 65433504 System.Threading.Tasks.TaskFactory+CompleteOnInvokePromise
00007ffde47cc988 167885 76872908 System.Byte[]
00007ffde47c6948 521353 80352802 System.String
0000007e3a16c2d0 1870425 1415374334 Free
所以转储大约是3GB,所以大约一半是可用内存。看着堆,我看到了:
!heapstat
Heap Gen0 Gen1 Gen2 LOH
Heap0 82248472 7354560 987275056 178834656
Heap1 93146552 6382864 857470096 129435960
Total 175395024 13737424 1844745152 308270616
Free space: Percentage
Heap0 40969256 146456 640426720 54829792 SOH: 63% LOH: 30%
Heap1 75943736 94448 550812312 54825216 SOH: 65% LOH: 42%
Total 116912992 240904 1191239032 109655008
所以我的小物件堆很碎片,特别是Gen2。在服务器上我可以看到gen2集合正在发生(使用性能计数器),但即使它们看起来像gen2堆也没有被压缩。即使服务器上只有1-2%的RAM可用,也不会压缩gen2堆。
对我来说,看起来我正在遭受这种记忆压力,因为堆是碎片。但是,我无法弄清楚碎片发生的原因或者为什么gen2无法压缩。一些自由空间的大小为6MB,所以我认为它肯定会压缩那些空间。
任何人都可以给我一些关于如何弄清楚我的堆如此碎片化的想法吗?我甚至在这里咆哮着正确的树吗?
非常感谢任何帮助,谢谢!
编辑1:
!gchandles
的细分是:
Handles:
Strong Handles: 4507
Pinned Handles: 58
Async Pinned Handles: 977
Ref Count Handles: 1
Weak Long Handles: 6087
Weak Short Handles: 724
答案 0 :(得分:0)
下一步是使用!gchandles
并查找固定句柄。这可能会识别锁定到特定内存位置的对象,因为某些本机代码(例如C ++)需要访问它。虽然垃圾收集可能会在内存中移动对象,但.NET不会更新C ++指针,因此固定是唯一的选择。
即使服务器上只有1-2%的RAM可用,也不会压缩gen2堆。
你在这里谈论物理RAM。从操作系统来看,我认为它尽可能使用可用资源,所以我期望 RAM始终100%使用。如果"未使用",我希望操作系统将其用于缓存。因此,物理RAM使用率实际上不能成为垃圾收集的指标。
另外,我不会太担心。如果.NET没有使用该内存,它将被操作系统交换到磁盘,因此可以将物理RAM用于其他目的。