我在ST中有一个计算过程,通过Data.Vector.Unboxed.Mutable分配内存。向量既没有被读取或写入,也没有在runST之外保留对它的任何引用(据我所知)。我的问题是,当我多次运行ST计算时,我有时似乎会保留向量的内存。
分配统计数据:
5,435,386,768 bytes allocated in the heap
5,313,968 bytes copied during GC
134,364,780 bytes maximum residency (14 sample(s))
3,160,340 bytes maximum slop
518 MB total memory in use (0 MB lost due to fragmentation)
我在使用不同的值和一个128MB的向量(未被使用,未在ST外返回或引用)下调用runST 20次进行计算。最大驻留空间看起来很好,基本上只有我的向量加上几MB的其他东西。但是总内存使用量表明我同时拥有四个向量副本。这与向量的大小完全成比例,对于256MB,我们可以得到预期的1030MB。
使用1GB的向量将耗尽内存(4x1GB + 超额空间>32位)。我不理解为什么RTS会保留看起来未使用、未引用的内存,而不是在分配失败之前进行垃圾回收。
使用+RTS -S运行显示如下:
Alloc Copied Live GC GC TOT TOT Page Flts
bytes bytes bytes user elap user elap
134940616 13056 134353540 0.00 0.00 0.09 0.19 0 0 (Gen: 1)
583416 6756 134347504 0.00 0.00 0.09 0.19 0 0 (Gen: 0)
518020 17396 134349640 0.00 0.00 0.09 0.19 0 0 (Gen: 1)
521104 13032 134359988 0.00 0.00 0.09 0.19 0 0 (Gen: 0)
520972 1344 134360752 0.00 0.00 0.09 0.19 0 0 (Gen: 0)
521100 828 134360684 0.00 0.00 0.10 0.19 0 0 (Gen: 0)
520812 592 134360528 0.00 0.00 0.10 0.19 0 0 (Gen: 0)
520936 1344 134361324 0.00 0.00 0.10 0.19 0 0 (Gen: 0)
520788 1480 134361476 0.00 0.00 0.10 0.20 0 0 (Gen: 0)
134438548 5964 268673908 0.00 0.00 0.19 0.38 0 0 (Gen: 0)
586300 3084 268667168 0.00 0.00 0.19 0.38 0 0 (Gen: 0)
517840 952 268666340 0.00 0.00 0.19 0.38 0 0 (Gen: 0)
520920 544 268666164 0.00 0.00 0.19 0.38 0 0 (Gen: 0)
520780 428 268666048 0.00 0.00 0.19 0.38 0 0 (Gen: 0)
520820 2908 268668524 0.00 0.00 0.19 0.38 0 0 (Gen: 0)
520732 1788 268668636 0.00 0.00 0.19 0.39 0 0 (Gen: 0)
521076 564 268668492 0.00 0.00 0.19 0.39 0 0 (Gen: 0)
520532 712 268668640 0.00 0.00 0.19 0.39 0 0 (Gen: 0)
520764 956 268668884 0.00 0.00 0.19 0.39 0 0 (Gen: 0)
520816 420 268668348 0.00 0.00 0.20 0.39 0 0 (Gen: 0)
520948 1332 268669260 0.00 0.00 0.20 0.39 0 0 (Gen: 0)
520784 616 268668544 0.00 0.00 0.20 0.39 0 0 (Gen: 0)
521416 836 268668764 0.00 0.00 0.20 0.39 0 0 (Gen: 0)
520488 1240 268669168 0.00 0.00 0.20 0.40 0 0 (Gen: 0)
520824 1608 268669536 0.00 0.00 0.20 0.40 0 0 (Gen: 0)
520688 1276 268669204 0.00 0.00 0.20 0.40 0 0 (Gen: 0)
520252 1332 268669260 0.00 0.00 0.20 0.40 0 0 (Gen: 0)
520672 1000 268668928 0.00 0.00 0.20 0.40 0 0 (Gen: 0)
134553500 5640 402973292 0.00 0.00 0.29 0.58 0 0 (Gen: 0)
586776 2644 402966160 0.00 0.00 0.29 0.58 0 0 (Gen: 0)
518064 26784 134342772 0.00 0.00 0.29 0.58 0 0 (Gen: 1)
520828 3120 134343528 0.00 0.00 0.29 0.59 0 0 (Gen: 0)
521108 756 134342668 0.00 0.00 0.30 0.59 0 0 (Gen: 0)
看起来我们的“活字节”超过了 ~128MB。
+RTS -hy
profile 基本上只是说我们分配了 128MB:
http://imageshack.us/a/img69/7765/45q8.png
我尝试在一个更简单的程序中复制这种行为,但即使使用 ST、包含 Vector 的 Reader,相同的 monad/program 结构等,简单的测试程序也没有显示出这种情况。当我简化我的大程序并删除显然完全不相关的代码时,这种行为最终也会停止。
问题:
- 我真的把这个向量保存了 20 次中的 4 次吗?
- 如果是的话,我该如何判断,因为
+RTS -Hy
和maximum residency
声称我没有,并且我该怎么做才能阻止这种行为? - 如果不是的话,为什么 Haskell 没有 GC 它并耗尽地址空间 / 内存,我该怎么做才能阻止这种行为?
谢谢!
$ ./foo +RTS -M256M
,以强制它更早地进行垃圾回收。但是,“在 runST 之外没有任何引用保留”可能是不正确的,如果是这种情况,需要查看代码以确定是否存在泄漏。 - Daniel FischerrunST
计算的返回类型是什么?可能存在一些隐藏的惰性。尝试使用deepseq
或者使用ghc-heap-view
进行调查。 - Joachim Breitner