为什么包装Data.Binary.Put monad会造成内存泄漏?

时间:2011-01-28 13:48:41

标签: memory haskell memory-leaks binary memory-management

我正在尝试将Data.Binary.Put monad包装到另一个monad中,以便稍后我可以问一些问题,例如“它要写多少字节”或“文件中的当前位置是什么”。但即便是非常微不足道的包装:

data Writer1M a = Writer1M { write :: P.PutM a }
or
data Writer2M a = Writer2M { write :: (a, P.Put) }

造成巨大的空间泄漏,程序通常崩溃(占用4GB内存后)。这是我到目前为止所尝试的:

-- This works well and consumes almost no memory.

type Writer = P.Put

writer :: P.Put -> Writer
writer put = put

writeToFile :: String -> Writer -> IO ()
writeToFile path writer = BL.writeFile path (P.runPut writer)

-- This one will cause memory leak.

data Writer1M a = Writer1M { write :: P.PutM a }

instance Monad Writer1M where
  return a = Writer1M $ return a
  ma >>= f = Writer1M $ (write ma) >>= \a -> write $ f a

type WriterM = Writer1M
type Writer = WriterM ()

writer :: P.Put -> Writer
writer put = Writer1M $ put

writeToFile :: String -> Writer -> IO ()
writeToFile path writer = BL.writeFile path (P.runPut $ write writer)
-- This one will crash as well with exactly the
-- same memory foot print as Writer1M

data Writer2M a = Writer2M { write :: (a, P.Put) }

instance Monad Writer2M where
  return a = Writer2M $ (a, return ())
  ma >>= f = Writer2M $ (b, p >> p')
                        where (a,p) = write ma
                              (b,p') = write $ f a

type WriterM = Writer2M
type Writer = WriterM ()

writer :: P.Put -> Writer
writer put = Writer2M $ ((), put)

writeToFile :: String -> Writer -> IO ()
writeToFile path writer = BL.writeFile path (P.runPut $ snd $ write writer)

我是Haskell的新手,这对我没有任何意义,但是封装monad看起来非常微不足道所以我猜我有一些明显的东西。

感谢您的光临。

更新: 以下是演示此问题的示例代码:http://hpaste.org/43400/why_wrapping_the_databinaryp

UPDATE2: 这个问题还有第二部分here

2 个答案:

答案 0 :(得分:4)

经过一番探索后,我发现问题似乎是使用二进制(>> =)来实现(>>)。以下对Writer1M monad实现的补充解决了这个问题:

  m >> k = Writer1M $ write m >> write k

虽然这个版本仍会泄漏内存:

  m >> k = Writer1M $ write m >>= const (write k)

查看binary's source,(>>)似乎明确地丢弃了第一个monad的结果。但不确定这究竟是如何防止泄漏的。我最好的理论是GHC否则会保留在PairS对象上,并且“a”引用会泄漏,因为它永远不会被查看。

答案 1 :(得分:2)

你有没有试过让monad更严格?例如。尝试使您的datatyp的构造函数严格/用newtype替换它们。

我不知道这里的确切问题是什么,但这是泄漏的常见原因。

PS:并尝试删除不必要的lambdas,例如:

  ma >>= f = Writer1M $ (write ma) >=> write . f