隔离大固定对象计数的来源

时间:2014-07-24 10:30:08

标签: .net debugging memory-leaks windbg

在捕获我们的一个.NET进程的性能监视指标时,我们注意到我们有大量的固定对象。具体来说,我们正在监控" .NET CLR内存" counter" 固定对象数"它的价值稳步上升到数千。总体而言,趋势是稳定的45度上升趋势线。由于这是一个可靠的可重复条件,我们使用WinDbg进行了内存转储,并且惊讶地发现我们只有23个固定项目与我们在PerfMon中看到的不匹配。这引出了以下问题:

  1. 为什么固定对象的PerfMon和GCHandles数量不同?

  2. 如果PerfMon是正确的,这不表示内存泄漏吗?如果是,我们如何找到它?

  3. !gchandles

    的结果

    注意:所有"固定"句柄都是System.Object[],它们似乎都包含Int64值

    Handles:
        Strong Handles:       183
        Pinned Handles:       23
        Async Pinned Handles: 3
        Ref Count Handles:    7
        Weak Long Handles:    16762
        Weak Short Handles:   481
        SizedRef Handles:     8
        Dependent Handles:    57
    

    来自!eeheap -gc

    的结果
    0:048> !eeheap -gc
    Number of GC Heaps: 4
    ------------------------------
    Heap 0 (0000000001fe2e20)
    generation 0 starts at 0x0000000103879b20
    generation 1 starts at 0x000000010385f528
    generation 2 starts at 0x00000000ff801000
    ephemeral segment allocation context: none
     segment     begin allocated  size
    00000000ff800000  00000000ff801000  00000001038cfb38  0x40ceb38(67955512)
    Large object heap starts at 0x00000004ff801000
     segment     begin allocated  size
    00000004ff800000  00000004ff801000  0000000501006c98  0x1805c98(25189528)
    Heap Size:               Size: 0x58d47d0 (93145040) bytes.
    ------------------------------
    Heap 1 (0000000001fe7d50)
    generation 0 starts at 0x0000000203d84328
    generation 1 starts at 0x0000000203c58a70
    generation 2 starts at 0x00000001ff801000
    ephemeral segment allocation context: none
     segment     begin allocated  size
    00000001ff800000  00000001ff801000  0000000204c9a298  0x5499298(88707736)
    Large object heap starts at 0x000000050f801000
     segment     begin allocated  size
    000000050f800000  000000050f801000  0000000510408e38  0xc07e38(12615224)
    Heap Size:               Size: 0x60a10d0 (101322960) bytes.
    ------------------------------
    Heap 2 (0000000001ff9590)
    generation 0 starts at 0x000000030360fb10
    generation 1 starts at 0x00000003036065f0
    generation 2 starts at 0x00000002ff801000
    ephemeral segment allocation context: none
     segment     begin allocated  size
    00000002ff800000  00000002ff801000  00000003042ff190  0x4afe190(78635408)
    Large object heap starts at 0x000000051f801000
     segment     begin allocated  size
    000000051f800000  000000051f801000  0000000520c38850  0x1437850(21198928)
    Heap Size:               Size: 0x5f359e0 (99834336) bytes.
    ------------------------------
    Heap 3 (00000000020152b0)
    generation 0 starts at 0x00000004036f9da8
    generation 1 starts at 0x00000004036f9a28
    generation 2 starts at 0x00000003ff801000
    ephemeral segment allocation context: none
     segment     begin allocated  size
    00000003ff800000  00000003ff801000  00000004038c3da0  0x40c2da0(67906976)
    Large object heap starts at 0x000000052f801000
     segment     begin allocated  size
    000000052f800000  000000052f801000  00000005305a1d88  0xda0d88(14290312)
    Heap Size:               Size: 0x4e63b28 (82197288) bytes.
    ------------------------------
    GC Heap Size:            Size: 0x1670eda8 (376499624) bytes.
    

1 个答案:

答案 0 :(得分:4)

该perf计数器实际上并未显示钉扎手柄的数量。它显示上次垃圾回收期间无法移动的对象数。

这是一个非常不同的数字。首先,它当然是陈旧的,所以你的minidump不一定是一个很好的匹配。另一方面,它还显示了不需要手柄的针脚。在Windbg报告中,您无法轻易看到这些内容。它们是具有[pinned]属性的局部变量。垃圾收集器在执行堆栈遍历时找回它们,这是一种非常有效的引脚方式。 C#中的 fixed 关键字创建它们。

看到这些类型的引脚增长,我能想到的可能原因有限。递归可以解释它。或者线程数越来越多,这是造成内存爆炸问题的常见原因。