生成不同整数的树会导致空间泄漏

Posted

技术标签:

【中文标题】生成不同整数的树会导致空间泄漏【英文标题】:Generating a tree of distinct integers results in a space leak 【发布时间】:2015-12-24 12:38:34 【问题描述】:

我想生成一个包含不同整数的树并找到它们的总和。代码如下:

-# LANGUAGE BangPatterns #-

import Control.Applicative
import Control.Monad.Trans.State

data Tree a = Leaf a | Branch (Tree a) a (Tree a)

new = get <* modify' (+ 1)

tree :: Integer -> Tree Integer
tree n = evalState (go n) 0 where
    go 0 = Leaf <$> new
    go n = Branch <$> go (n - 1) <*> new <*> go (n - 1)

sumTree = go 0 where
    go !a (Leaf n)       = a + n
    go !a (Branch l n r) = go (go (a + n) l) r

main = print $ sumTree (tree 20)

使用-O2 编译结果

     348,785,728 bytes allocated in the heap
     147,227,228 bytes copied during GC
      34,656,860 bytes maximum residency (13 sample(s))
          35,468 bytes maximum slop
              72 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0       565 colls,     0 par    0.764s   1.024s     0.0018s    0.0071s
  Gen  1        13 colls,     0 par    0.936s   1.014s     0.0780s    0.3214s

  INIT    time    0.000s  (  0.001s elapsed)
  MUT     time    0.936s  (  0.764s elapsed)
  GC      time    1.700s  (  2.038s elapsed)
  EXIT    time    0.000s  (  0.002s elapsed)
  Total   time    2.636s  (  2.805s elapsed)

  %GC     time      64.5%  (72.7% elapsed)

  Alloc rate    372,631,936 bytes per MUT second

  Productivity  35.5% of total user, 33.4% of total elapsed

为什么会出现这种空间泄漏?怎么去掉?

【问题讨论】:

import Control.Monad.Trans.State.Strict 代替,因为你不能在这里使用奇怪的额外懒惰。这似乎是最有可能的问题。你可能会或可能不会真正受益于那种花哨的sumTree,这取决于它的倾斜方式(我认为)。 呃...我想你可以从额外的懒惰中受益,但必须非常小心地求和。我一般不会推荐这种方法——太脆弱了。 @dfeuer,这是我尝试的第一件事。它稍微好一点,但仍然有泄漏。我写的best version 使用严格和惰性状态(但没有明确的State)。速度是原来的两倍,使用的总内存为 27 MB,但仍然是 GC 时间的一半。 你的sumTree 强制左子树(至少)因为(a + n) 在严格的累加器中(n 是由计算左树的State 计算输出的)。如果你写一个不带累加器的sumTree 并切换到Int-s,它会在 2 MB 内存中运行。我不确定为什么Integer 即使在这种情况下也会泄漏空间;我可能会看看它,稍后再写一个答案,因为我现在很忙。 原来Integer 泄漏空间是因为它构建了整个(+) thunk 树(“经典”泄漏),而Int 被拆箱并变得严格。左侧总和上的seq 或爆炸图案可修复它。我同意它相当脆弱。 【参考方案1】:

任何时候你构建一棵树,你都应该尝试找到一种完全自上而下工作的方法。这通常对惰性、并发性、缓存利用率、GC 有效性等有好处。您构建的树只是按顺序编号的完整二叉树。我建议您考虑使用以下签名并进行一些位移:

tree :: Bits b => Int -> Tree b

您可以分解一个作为起点的辅助函数。

【讨论】:

以上是关于生成不同整数的树会导致空间泄漏的主要内容,如果未能解决你的问题,请参考以下文章

QByteArray导致的内存泄漏问题

内存溢出与内存泄漏区别

android 内存泄漏问题

内存泄漏与内存溢出

内存泄漏和内存溢出的区别

java内存泄漏与处理