由于固定的 GC 句柄/没有可见的 gc 根导致内存泄漏

Posted

技术标签:

【中文标题】由于固定的 GC 句柄/没有可见的 gc 根导致内存泄漏【英文标题】:Memory leak because of pinned GC handles / no gc root visible 【发布时间】:2021-08-09 11:49:37 【问题描述】:

使用非托管 .net 组件时固定 GC 句柄的原因是什么?这种情况不时发生,无需更改任何代码或其他任何东西。在调查这个问题时,我看到很多固定的 GC-Handles

这些句柄似乎在整个应用程序生命周期内都在内存中。在这种情况下,库是 GdPicture (14)。有什么方法可以调查为什么这些实例没有被清理?我到处都在使用Dispose()/using,但在托管代码中找不到任何 GC 根。

非常感谢!

编辑

另一个奇怪的行为是,任务管理器显示应用程序使用大约 6GB 内存,而内存分析器显示使用 400MB(红线是活动字节)

【问题讨论】:

【参考方案1】:

使用非托管 .net 组件时固定 GC 句柄的原因是什么?

使用非托管代码时需要固定。它可以防止在垃圾回收期间移动对象,以便非托管代码可以拥有指向它的指针。垃圾收集器将更新所有 .NET 引用,但不会更新非托管指针值。

有什么方法可以调查为什么没有清理这些实例?

没有。原因总是:代码中存在错误。无论是您的代码(假设是第一个)还是在第 3 方库中(经常使用库,很可能库中的泄漏已经被其他人发现)。

我正在使用 Dispose()/到处使用

好像你漏掉了一个或者它没有使用一次性图案。

另一个奇怪的行为是,任务管理器显示应用程序使用大约 6GB 内存,而内存分析器显示使用 400MB(红线是活动字节)

.NET 内存分析器可能只显示内存的 .NET 部分 (400 MB) 而忽略其余部分 (5600 MB)。

任务管理器对 .NET 不感兴趣。它主要关心物理 RAM,这就是为什么任务管理器通常不是一个好的分析工具。您不想分析物理 RAM,而是要分析虚拟内存。

要查找内存泄漏,请使用 Process Explorer 并显示“Private Bytes”和“Virtual size”列。 Process Explorer 还可以显示每个进程随时间变化的图表。

如何进行?

暂时忘记非托管泄漏。使用 .NET 分析器,该分析器能够拍摄内存快照并允许您查看内部的每个单独对象以及统计信息。

尝试找出以一致的方式制造更多泄漏所需的步骤。那么

    拍摄快照 重复泄漏过程 10 次 拍摄快照 再重复泄漏过程 10 次 快照

比较第 1 步和第 3 步的快照。检查是否存在差异为 10 的倍数的托管类型。比较第 3 步和第 5 步的快照。再次检查相同类型。必须是 10 的倍数。一个方法运行 10 次不能泄漏 7 个对象。

根据有关泄漏过程(调用哪些方法)和托管类型的内部知识,对使用受影响类型的位置进行代码审查。确保正确处置或释放它。

【讨论】:

以上是关于由于固定的 GC 句柄/没有可见的 gc 根导致内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

Conservative GC (Part one)

什么是类的 GC 根?

简述GC回收机制

依赖包中System.gc()导致Full GC

如何在 Haskell 中查找和修复由于 GC 导致的性能问题?

记一次 CMS GC导致 FULL GC 时间开销很大的排查