【发布时间】:2017-05-09 11:07:12
【问题描述】:
我编写了一个名为amqp-worker 的库,它提供了一个名为worker 的函数,该函数轮询消息队列(如RabbitMQ)以获取消息,并在找到消息时调用处理程序。然后它回到轮询。
它正在泄漏内存。我已经对其进行了分析,图表显示PAP(部分函数应用程序)是罪魁祸首。 我的代码中的漏洞在哪里?在IO 和forever 循环时如何避免泄漏?
这里有一些相关的功能。 The full source is here.
Example Program。这泄露了
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)
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)
consumeNext :: (FromJSON msg, MonadBaseControl IO m) => Microseconds -> Connection -> Queue key msg -> m (ConsumeResult msg)
consumeNext pd conn queue =
poll pd $ consume conn queue
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
【问题讨论】:
-
你的 ghc 版本是什么,你是如何编译的?
-
它设置为 lts-7.3,所以这是 GHC 8.0.1。我正在使用 stack install --profile 进行编译。但是我通过正常的堆栈安装得到了内存泄漏。使用堆栈模板中的默认 ghc 选项:-threaded -rtsopts -with-rtsopts=-N
-
这个例子远非最小——你在你的例子程序中导入你的整个库(
Network.AMQP.Worker)。就目前而言,这太宽泛了。 -
这是我第一次寻找内存泄漏。我想知道是否有一种好方法可以确定它们在这样的程序中的位置
-
我会尝试摆脱 monad 类型类,只使用
IO。我不确定这是否可能是问题所在,但最好少担心一件事情。
标签: haskell recursion memory-leaks profiling