Reputation: 2239
I'm playing with some basic haskell network stuff but there is a problem which drives me nuts. The following quite simple server code seems to leak memory and I just don't know why.
First I fork the accept-method and just echo all incoming messages. I've tested the server with the Apache Benchmark Tool ab. But after a few tests using ab the server process starts to leak memory, not much but continuously. I would guess roughly 1 MB every 10000 requests.
The question is: Is this some kind of internal Garbage Collector optimization or does it indeed leak memory ? I've tested the program up to 200 MB memory usage, still growing.
In this example I'm using strict ByteStrings. Another test I did was using handle-based IO, which didn't caused the memory to grow over 3 MB in total.
Using lazy IO (ByteString.Lazy) caused the same problem but even faster. I'm using GHC 7.6 (Windows 8).
echoClient :: Socket -> IO ()
echoClient nextSock = do
-- Get a stream of bytes
stream <- NetStrictBS.recv nextSock 120
-- Echo back
NetStrictBS.send nextSock stream
-- Kill the socket
sClose nextSock
acceptClients :: Socket -> IO ()
acceptClients sock = do
-- Start with zero index
loop sock 0
where
loop sock sockId = do
-- Accept socket
(nextSock, addr) <- accept sock
-- Disable Nagle
setSocketOption nextSock NoDelay 1
-- Process client
echoClient nextSock
-- Accept next client
loop sock (sockId + 1)
main = withSocketsDo $ do
-- Setup server socket
sock <- tcpSock
setSocketOption sock ReuseAddr 1
bindSocket sock (SockAddrInet 1337 iNADDR_ANY)
listen sock 128
-- Fork the acceptor
forkIO $ acceptClients sock
print "Server running ... " >> getLine >>= print
Upvotes: 4
Views: 785
Reputation: 2239
I've just spoke to an colleague and he told me that the problem is this section
loop sock (sockId + 1)
I'm buildung up thunks without ever evaluting, which definitely fills up the heap. I hope this helps other people facing similiar problems.
Upvotes: 6