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

时间:2014-07-24 16:49:34

标签: haskell memory-leaks profiling ghc

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

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

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

使用+RTS -hc -xt -p

运行

screenshot of profiler output

使用+RTS -hr -xt -p

运行

screenshot of profiler output

在此测试运行期间,守护程序随后消耗超过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)
[...]

更新2:我发现了问题的根源,内存泄漏是由handleToFd引起的(有关 unix 库,请参阅this issue )。我只是想知道如何能够更有效地查明这种泄漏(可能发生在外国代码中)。

1 个答案:

答案 0 :(得分:2)

虽然我不熟悉Haskell守护程序本身,回答你的问题"如何更有效地查明这样的漏洞",有可能使用

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

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

LD_PRELOAD="yourlibrary.so" valgrind your-executable