【问题标题】:Why wrapping the Data.Binary.Put monad creates a memory leak?为什么包装 Data.Binary.Put monad 会造成内存泄漏?
【发布时间】:2011-06-17 06:23:28
【问题描述】:

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

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

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

-- 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

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

【问题讨论】:

  • 你使用什么编译器标志?
  • 在你询问后我尝试了 -O2(我之前没有使用过)但内存占用没有改变。
  • 你能发布一个简单的测试程序,这样这里的其他人就不必自己构建了吗?
  • 好主意,我很快就会编译一些东西。我可以以某种方式将其发布在 stackoverflow 上还是应该使用 hpaste 代替?
  • 我已经尝试过您的示例,并且使用我的 GHC 6.12.3 和 -O2 标志,两个版本都显示几乎相同的时间/空间行为。用newtype 替换“有问题的”包装器中的data 可以进一步减少差异。没有 -O2 它绝对是内存贪婪的。你确定你用-O2重新编译了吗?试试-O2 -fforce-recomp

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


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

    你有没有尝试让 monad 更严格?例如。尝试使您的数据类型的构造函数严格/用新类型替换它们。

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

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

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

    【讨论】:

    • 从 data 更改为 newtype 也是#haskell 的好人所建议的,不幸的是,按照您的建议更改并删除 lambda 并没有改变内存占用空间。不过谢谢你的建议。
    • 是的,结果如下:i.imgur.com/4Q2E3.png,当我使用其中一个包装器时会出现黄色区域。
    • 很奇怪。我想我帮不了你。
    • 我猜 Writer1M 无意中充当了一个保持器,持有一个指向输出的指针,从而阻止 GC 沿途收获字节串。尝试使用保持器分析运行,看看是否能告诉你什么。
    猜你喜欢
    • 2010-10-11
    • 1970-01-01
    • 2019-09-08
    • 2017-07-01
    • 2011-10-25
    • 2014-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多