Home > Enterprise >  How do I get rid of this memory leak in Haskell?
How do I get rid of this memory leak in Haskell?

Time:07-29

I'm trying to find out why the following code has a memory leak:

module Main where

import System.IO

func :: Int -> Int -> ([Int], Int)
func input 0 = ([], input)
func input numTimes = do
    let (rest, ret) = func (input   1) (numTimes - 1)
    ((input : rest), ret)

main :: IO ()
main = do
    hSetBuffering stdout LineBuffering
    let (list, final) = func 0 10000000000
        listStr = map (\x -> (show x)    "\n") list
    putStr (foldr (  ) "" listStr)
    putStr (show final)

printStrs :: [String] -> String -> IO ()
printStrs [] str = do
    putStrLn str
printStrs (first : rest) str = do
    putStr first
    printStrs rest str

When I compile it with ghc --make Main and run it, the top command shows it eating up more and more memory even though the amount of memory it uses should be constant because of lazy evaluation. I've tried using the printStrs function I wrote instead, and it still eats up all memory. I've tried using ghci on the code and using :sprint to print out the thunks from func and it seems like the thunks aren't increasing the amount of memory used for each evaluation of an element in the list.

I honestly don't know what else to do.

CodePudding user response:

The problem is that func will build a huge list and laziness will not be able to avoid it. It reminds me of continuation passing where the order of computations are sequentialized.

I think, the part with foldr is responsible for the memory consumption. By avoiding it and compiling it with ghc -O3, the memory usage is constant in my test:

module Main where

import System.IO

func :: Int -> Int -> ([Int], Int)
func input 0 = ([], input)
func input numTimes = do
    let (rest, ret) = func (input   1) (numTimes - 1)
    ((input : rest), ret)

main :: IO ()
main = do
    hSetBuffering stdout LineBuffering
    let (list, final) = func 0 10000000000
    mapM_ (putStrLn . show) list
    putStr (show final)

In ghci, it still blows the memory. But it might be because the interpreter is not able to optimize the recursion away.

  • Related