Sea*_*ess 16 recursion profiling haskell memory-leaks
我编写了一个名为amqp-worker的库,它提供了一个调用worker消息队列(如RabbitMQ)消息的函数,在发现消息时调用处理程序.然后它回到投票.
这是在泄漏记忆.我已经对它进行了分析,图表说PAP(部分功能应用程序)是罪魁祸首.我的代码泄漏在哪里?如何避免泄漏循环中时IO有forever?
这是一些相关的功能.完整的来源是在这里.
示例程序.这个漏洞
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 :: (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 的部分应用程序链
.