在 GHC 的堆分析器的视图之外可能发生哪些内存泄漏

What memory leaks can occur outside the view of GHC's heap profiler

我有一个程序表现出内存泄漏行为。它逐渐占用所有系统内存,直到它填满所有交换 space,然后操作系统将其杀死。这种情况每隔几天发生一次。

我已经以多种方式(-hy、-hm、-hc)对堆进行了广泛的分析,并尝试限制堆大小(-M128M)调整了世代数(-G1),但无论我做什么堆大小似乎保持不变并且总是很低(以 kB 为单位,而不是 MB 或 GB)。然而,当我在htop中观察程序时,它的驻留内存却在稳步攀升。

这向我表明内存泄漏来自 GHC 堆之外的某个地方。我的程序使用了依赖项,特别是 Haskell 的 yaml 库,它包装了 C 库 libyaml,泄漏可能在于它必须分配给对象的外部指针的数量通过 libyaml.

我的问题有三个:

  1. Haskell 程序中除了 GHC 堆之外还有哪些地方可能发生内存泄漏?
  2. 我可以使用什么工具来追踪这些?
  3. 需要对我的源代码进行哪些更改才能避免这些类型的泄漏,因为它们似乎不同于 Haskell 中更常见的 space 泄漏?

这听起来确实像是没有正确完成外部指针。这有几个可能的原因:

  1. 底层 C 库没有正确释放内存。
  2. Haskell 库没有正确设置完成。
  3. ForeignPtr 个对象没有被释放。

我认为它实际上很有可能是选项 3。如果 RTS 在第一代 GC 中始终找到足够的内存,那么它就不会打扰 运行 进行主要收集。幸运的是,这是最容易诊断的。每隔一段时间就让你的程序 运行 System.Memory.performGC。如果这修复了它,那么您就找到了错误并且可以调整您想要执行此操作的频率。

另一个可能的问题是,您可能在长寿命的 thunk 或其他闭包中存在外部指针。确保你没有。


使用包装的 C 库时,一个特别强烈的可能性是包装函数将 return ByteString 其底层数组由 C 代码分配。因此,您从 yaml 返回的任何 ByteString 都可能是堆外的。