如何解决Gen2堆碎片

sho*_*der 5 .net c# garbage-collection heap-fragmentation memory-fragmentation

我正在运行一个服务HTTP请求的C#应用​​程序。我最近注意到,它占用的内存比我预期的要多。我抓起一些转储,将它们弹出到Windbg中,发现大部分内存都标记为“空闲”:

!dumpheap -stat
...
00007ffde4783630   681599     65433504 System.Threading.Tasks.TaskFactory+CompleteOnInvokePromise
00007ffde47cc988   167885     76872908 System.Byte[]
00007ffde47c6948   521353     80352802 System.String
0000007e3a16c2d0  1870425   1415374334      Free
Run Code Online (Sandbox Code Playgroud)

因此,转储约为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
Run Code Online (Sandbox Code Playgroud)

因此,我的小对象堆非常分散,特别是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
Run Code Online (Sandbox Code Playgroud)

Tho*_*ler 0

下一步是使用!gchandles并寻找固定的手柄。这可能会识别锁定到特定内存位置的对象,因为某些本机代码(例如 C++)需要访问它。虽然垃圾收集可能会在内存中移动对象,但 .NET 不会更新 C++ 指针,因此固定是唯一的选择。

即使服务器上只有 1-2% 的可用 RAM,gen2 堆也不会被压缩。

您在这里谈论的是物理 RAM。对于操作系统,我期望它尽可能好地使用可用资源,因此我期望RAM 始终 100% 使用。如果它“未使用”,我希望操作系统将其用于缓存。因此,物理 RAM 使用情况不能真正作为垃圾回收的指标。

另外,我也不会太担心。如果 .NET 不使用内存,操作系统会将其交换到磁盘,从而释放物理 RAM 用于其他目的。