我目前正在尝试将 XML 文件的内容读入 Map Int (Map Int String)
并且它工作得很好(使用 HaXml)。但是,我对程序的内存消耗不满意,问题似乎出在垃圾回收上。
这是我用来读取 XML 文件的代码:
type TextFile = Map Int (Map Int String)
buildTextFile :: String -> IO TextFile
buildTextFile filename = do content <- readFile filename
let doc = xmlParse filename content
con = docContent (posInNewCxt filename Nothing) doc
return $ buildTF con
我的猜测是content
即使在返回后也保存在内存中,虽然它不需要是(当然它也可以是doc
或 con
)。我得出这个结论是因为对于非常大的 XML 文件,内存消耗会迅速上升,虽然生成的 TextFile
只是单例映射的单例映射(使用特殊的测试文件,一般它是不同的,当然).所以最后,我有一个 Map Int String
的 Map
,里面只有一个字符串,但是内存消耗高达 19 MB。
使用严格的应用程序 ($!
) 或使用 Data.Text
而不是 TextFile
中的 String
不会不要改变任何东西。
所以我的问题是:有什么方法可以告诉编译器字符串 content
(或 doc
或 con
)不是不再需要它并且它可以被垃圾收集?
更一般地说:如何在不进行所有猜测的情况下找出问题的真正来源?
编辑:正如 FUZxxl 所建议的,我尝试使用 deepseq 并更改了 buildTextFile
的第二行,如下所示:
let doc = content `deepseq` xmlParse filename content
不幸的是,这并没有真正改变任何东西(或者我用错了吗?)...
最佳答案
不要猜测什么在消耗内存,要确定
第一步是确定哪些类型消耗的内存最多。您可以在 SO 上看到大量堆分析示例或阅读 GHC manual .
强制计算
如果问题是惰性求值(您正在构建一个可以计算 XML 文档类型并将字符串也留在堆中的堆上 thunk),那么使用 rnf 和 seq:
buildTextFile :: String -> IO TextFile
buildTextFile filename = do content <- readFile filename
let doc = xmlParse filename content
con = docContent (posInNewCxt filename Nothing) doc
res = buildTF con
return $ rnf res `seq` res
或者只使用 bang 模式 (!res = buildTF con
),无论哪种方式都应该强制 thunk 并允许 GC 收集 String
。
关于haskell - 惰性 IO - 字符串未被垃圾收集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6764075/