0

首先是问题 - 以下行为是逻辑上预期的,还是要为 GHC 报告的错误?

下面的代码将泄漏内存(在 上测试ghc-8.8.4),因为ghc似乎join point在循环结束时添加并跳转到它,构建堆栈。

{-# OPTIONS_GHC -fno-full-laziness #-}

module Main where

import Control.Concurrent.Async (async,waitCatch)
import Data.IORef
import GHC.Conc

main :: IO ()
main = do
  val <- newIORef 0 :: IO (IORef Int)
  let loop1 = do
          cval <- readIORef val
          threadDelay 1
          writeIORef val (cval + 1)
          case cval > 100000000 of
            True -> error "done"
            False -> loop1
          loop1 -- Deliberately, add this to cause space leak, but this statement is never executed because of case branching above

  loop1Async <- async loop1
  res <- waitCatch loop1Async

  return ()

编译-O2 -rtsopts -threaded和运行+RTS -s -hT -N会因为堆栈的增长而显示空间泄漏。

查看核心输出,似乎泄漏是由于join(我猜它是 a join point)并且在循环结束时跳转到它会增加堆栈(如果我已经正确读取了核心)。删除最后一条语句loop1可以修复泄漏。

ghc core输出在这里

更新:根据评论中的反馈,这似乎是合乎逻辑的行为,而不是ghc. 因此,最好有一个解释堆栈增加的答案。这有助于我们了解这里发生了什么。ghc core输出已在上面发布。

4

1 回答 1

1

我当然希望该行会导致使用堆栈空间。额外的调用loop1几乎无关紧要。

将测试常数更改为10而不是更大的数字,并将return ()分支替换为print cval.

比较使用和不使用“从未执行”语句获得的输出。您可能会发现它的执行程度比您想象的要多。

于 2021-08-28T02:39:13.777 回答