调试堆分析中未显示的内存泄漏

Pet*_*lák 37 profiling haskell memory-leaks ghc

我正在研究接收和处理JSON请求的Haskell守护程序.虽然守护进程的操作很复杂,但主要结构有意保持简单:它的内部状态只是IORef一个数据结构,所有线程都对此执行原子操作IORef.然后有几个线程在触发器上取值时用它做一些事情.

问题是守护进程泄漏了内存,我无法找到原因.它肯定与请求相关:当守护进程每秒收到多个请求时,它会泄漏大小为1MB/s(由Linux工具报告).内存消耗稳步增长.没有请求,内存消耗保持不变.

令我感到困惑的是,这些都没有在GHC分析中显示出来.要么我在配置文件参数中缺少某些内容,要么内存被其他内容消耗:

运行+RTS -hc -xt -p:

profiler输出的屏幕截图

运行+RTS -hr -xt -p:

profiler输出的屏幕截图

在此测试运行期间,守护程序随后消耗超过1GB.因此,分析数据显然与实际消耗的内存数量级不对应.(我知道RTS,GC和性能分析本身会增加实际内存消耗,但这种差异太大了,并不符合不断增加的消耗.)

我已经尝试rnf了守护进程内的所有状态数据IORef,以及解析的JSON请求(以避免部分JSON字符串保留在某处),但没有太大的成功.

欢迎任何想法或建议.

更新:守护程序没有运行-threaded,因此没有操作系统级别的线程.

GC统计信息更接近堆分析而不是Linux报告的数字:

    Alloc    Copied     Live    GC    GC     TOT     TOT  Page Flts
    bytes     bytes     bytes  user  elap    user    elap
[...]
  5476616     44504   2505736  0.00  0.00   23.21  410.03    0    0  (Gen:  0)
 35499296     41624   2603032  0.00  0.00   23.26  410.25    0    0  (Gen:  0)
 51841800     46848   2701592  0.00  0.00   23.32  410.49    0    0  (Gen:  0)
 31259144     36416   2612088  0.00  0.00   23.40  410.61    0    0  (Gen:  0)
 53433632     51976   2742664  0.00  0.00   23.49  412.05    0    0  (Gen:  0)
 48142768     50928   2784744  0.00  0.00   23.54  412.49    0    0  (Gen:  0)
[...]
Run Code Online (Sandbox Code Playgroud)

更新2:我发现了问题的根源,内存泄漏是造成handleToFd(见这个问题对于UNIX库).我只是想知道如何更有效地查明这样的泄漏(可能发生在外国代码中).

小智 2

虽然我不熟悉 Haskell 守护进程本身,但回答你的问题“如何更有效地查明此类泄漏”,也许可以使用

valgrind --leak-check=yes haskelldaemon(如果使用调试信息编译它会更好),

或者,如果泄漏发生在共享库中,请尝试

LD_PRELOAD="yourlibrary.so" valgrind your-executable