为什么包装 Data.Binary.Put monad 会造成内存泄漏? [英] Why wrapping the Data.Binary.Put monad creates a memory leak?

查看:17
本文介绍了为什么包装 Data.Binary.Put monad 会造成内存泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将 Data.Binary.Put monad 包装到另一个中,以便稍后我可以问它诸如它将要写入多少字节"或文件中的当前位置"之类的问题.但即使是非常琐碎的包装,例如:

I'm trying to wrap the Data.Binary.Put monad into another so that later I can ask it questions like "how many bytes it's going to write" or "what is the current position in file". But even very trivial wraps like:

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

造成巨大的空间泄漏,程序通常会崩溃(在占用 4GB 内存后).这是我迄今为止尝试过的:

create a huge space leak and the program usually crashes (after taking up 4GB of RAM). Here is what I've tried so far:

-- 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 看起来很微不足道,所以我猜我缺少一些明显的东西.

I'm new to Haskell and this makes no sence to me, but the wrapper monads seem very trivial so I'm guessing there is something obvious I'm missing.

感谢您的关注.

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

UPDATE: Here is a sample code that demonstrates the problem: http://hpaste.org/43400/why_wrapping_the_databinaryp

更新2:这个问题还有第二部分 这里.

UPDATE2: There is also a second part to this question here.

推荐答案

摸索了一下,发现问题好像是使用二进制的(>>=)来实现(>>).对 Writer1M monad 实现的以下添加解决了该问题:

After poking around for a bit, I found that the problem seems to be the usage of binary's (>>=) to implement (>>). The following addition to the Writer1M monad implementation solves the problem:

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

然而这个版本仍然泄漏内存:

Whereas this version still leaks memory:

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

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

Looking at binary's source, (>>) seems to discard the result of the first monad explicitly. Not sure how exactly this prevents the leak, though. My best theory is that GHC otherwise holds onto the PairS object, and the "a" reference leaks because it never gets looked at.

这篇关于为什么包装 Data.Binary.Put monad 会造成内存泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆