我有一个程序显示内存泄漏的行为。它会逐渐占用所有的系统内存,直到填满所有的交换空间,然后操作系统就会杀死它。这种情况每隔几天就会发生一次。
我已经用多种方式(-hy、-hm、-hc(对堆进行了广泛的分析,并尝试限制堆大小(-M128M(调整代数(-G1(,但无论我做什么,堆大小都是恒定的,而且总是很低(以kB而非MB或GB为单位(。然而,当我在htop中观察这个程序时,它的常驻记忆稳步攀升。
这向我表明,内存泄漏来自GHC堆之外的某个地方。我的程序利用了依赖项,特别是Haskell的yaml
库,它封装了C库libyaml
,泄漏可能是因为它具有指向libyaml
分配的对象的外部指针的数量。
我的问题有三个:
- 在Haskell程序中,除了GHC堆之外,还有哪些地方可以泄漏内存
- 我可以使用什么工具来追踪这些信息
- 需要对我的源代码进行哪些更改才能避免这些类型的泄漏,因为它们似乎与Haskell中更常见的空间泄漏不同
这听起来确实像是外部指针没有正确完成。这可能有几个原因:
- 底层的C库无法正确释放内存
- Haskell库没有正确设置终结
ForeignPtr
对象没有被释放
我认为实际上很有可能是选项3。如果RTS在第一代GC中始终找到足够的内存,那么它就不会麻烦运行一个主要的集合。幸运的是,这是最容易诊断的。只是让你的程序每隔一段时间运行System.Memory.performGC
。如果修复了它,你就发现了这个错误,并且可以调整你想做的频率。
另一个可能的问题是,在长寿命的thunk或其他闭包中可能存在外部指针。确保你没有。
使用包装的C库时,一种特别强烈的可能性是包装函数将返回其底层数组由C代码分配的ByteString
。因此,您从yaml
返回的任何ByteString
都可能是堆外的。