递归IO功能中的内存泄漏 - PAP

Sea*_*ess 16 recursion profiling haskell memory-leaks

我编写了一个名为amqp-worker的库,它提供了一个调用worker消息队列(如RabbitMQ)消息的函数,在发现消息时调用处理程序.然后它回到投票.

这是在泄漏记忆.我已经对它进行了分析,图表说PAP(部分功能应用程序)是罪魁祸首.我的代码泄漏在哪里?如何避免泄漏循环中时IOforever

在此输入图像描述

这是一些相关的功能.完整的来源是在这里.

示例程序.这个漏洞

main :: IO ()
main = do
  -- connect
  conn <- Worker.connect (fromURI "amqp://guest:guest@localhost:5672")

  -- initialize the queues
  Worker.initQueue conn queue
  Worker.initQueue conn results

  -- publish a message
  Worker.publish conn queue (TestMessage "hello world")

  -- create a worker, the program loops here
  Worker.worker def conn queue onError (onMessage conn)
Run Code Online (Sandbox Code Playgroud)

工人

worker :: (FromJSON a, MonadBaseControl IO m, MonadCatch m) => WorkerOptions -> Connection -> Queue key a -> (WorkerException SomeException -> m ()) -> (Message a -> m ()) -> m ()
worker opts conn queue onError action =
  forever $ do
    eres <- consumeNext (pollDelay opts) conn queue
    case eres of
      Error (ParseError reason bd) ->
        onError (MessageParseError bd reason)

      Parsed msg ->
        catch
          (action msg)
          (onError . OtherException (body msg))
    liftBase $ threadDelay (loopDelay opts)
Run Code Online (Sandbox Code Playgroud)

consumeNext

consumeNext :: (FromJSON msg, MonadBaseControl IO m) => Microseconds -> Connection -> Queue key msg -> m (ConsumeResult msg)
consumeNext pd conn queue =
    poll pd $ consume conn queue
Run Code Online (Sandbox Code Playgroud)

轮询

poll :: (MonadBaseControl IO m) => Int -> m (Maybe a) -> m a
poll us action = do
    ma <- action
    case ma of
      Just a -> return a
      Nothing -> do
        liftBase $ threadDelay us
        poll us action
Run Code Online (Sandbox Code Playgroud)

Rom*_*aka 15

这是一个非常简单的示例,演示了您的问题:

main :: IO ()
main = worker

{-# NOINLINE worker #-}
worker :: (Monad m) => m ()
worker =
  let loop = poll >> loop
  in loop

poll :: (Monad m) => m a
poll = return () >> poll
If you remove the `NOINLINE`, or specialize `m` to
`IO` (while compiling with `-O`), the leak goes away.
Run Code Online (Sandbox Code Playgroud)

我写了一篇详细的博客文章,说明为什么这段代码会泄漏内存.正如Reid在他的回答中指出的那样,快速摘要是代码创建并记住>>s 的部分应用程序链 .

我还提交了一张关于此的ghc票.