cs240:recDir 在两种类似情况下使用的额外 150 MB

cs240: the extra 150 MB used by recDir under two similar circumstances

我正在浏览 Functional Systems in Haskell 的幻灯片。在那堂课中,定义了一个函数 recDir2,它使用 unsafeInterleaveIO 递归地列出目录中的所有文件:

import qualified Data.ByteString.Lazy       as L

recDir2 :: FilePath -> IO [FilePath]
recDir2 dir = do
  ds <- openDirStream dir
  let protect m = m `onException` closeDirStream ds

      nextName = unsafeInterleaveIO $
                 protect (readDirStream ds) >>= checkName

      checkName "" = closeDirStream ds >> return []
      checkName "." = nextName
      checkName ".." = nextName
      checkName name = getSymbolicLinkStatus path >>= checkStat path
          where path = dir </> name

      checkStat path stat
          | isRegularFile stat = liftM (path :) nextName
          | isDirectory stat =
              liftM2 (++) (protect $ recDir2 path) nextName
          | otherwise = nextName

  nextName

在哪里

readFiles :: [FilePath] -> IO L.ByteString
readFiles [] = return L.empty
readFiles (f:fs) = liftM2 L.append (L.readFile f)
                   (unsafeInterleaveIO $ readFiles fs)

后面两种看似相等的情况都会用到这个函数:

*Main> recDir2 "/usr/include" >>= readFiles >>= print . L.length

*Main> x <- recDir2 "/usr/include" >>= readFiles
*Main> L.length x

然而在幻灯片中说第二种情况使用额外的 150 MB,但我看不出原因。是什么导致了这种所谓的额外内存使用?

将此与以下内容进行比较:

> let xs = [1..999999999]
> length xs

> length [1..999999999]

在第二种情况下,垃圾收集器可以在列表被使用时清理它,因此您使用的内存非常少。但在第一种情况下,您仍然持有对 xs 的引用,并且您可以随时输入 xs !! 555。所以垃圾收集器必须为你在内存中保留 xs 的全部内容。

同样在您的示例中,一个版本引用了 x,以后可能会重复使用,因此必须保留 x 的内容。