给定程序:

import Language.Haskell.Exts.Annotated -- from haskell-src-exts
import System.Mem
import System.IO
import Control.Exception

main :: IO ()
main = do
  evaluate $ length $ show $ fromParseResult $ parseFileContents $ "data C = C {a :: F {- " ++ replicate 400000 'd' ++ " -}     }"
  performGC
  performGC
  performGC

在我运行时使用GHC 7.0.3:
$ ghc --make Temp.hs -rtsopts && Temp.exe +RTS -G1 -S
    Alloc    Copied     Live    GC    GC     TOT     TOT  Page Flts
    bytes     bytes     bytes  user  elap    user    elap
 ...
 29463264        64   8380480  0.00  0.00    0.64    0.85    0    0  (Gen:  0)
       20        56   8380472  0.00  0.00    0.64    0.86    0    0  (Gen:  0)
        0        56   8380472  0.00  0.00    0.64    0.87    0    0  (Gen:  0)
    42256       780     33452  0.00  0.00    0.64    0.88    0    0  (Gen:  0)
        0                      0.00  0.00
performGC调用似乎使8Mb的内存保持 Activity 状态,即使所有内存似乎都已耗尽。怎么会?

(没有-G1,我看到的是10Mb的结尾,这我也无法解释。)

最佳答案

这是我所看到的(在最后一个print之前插入performGC之后,以帮助在发生情况时进行标记。

   524288    524296  32381000  0.00  0.00    1.15    1.95    0    0  (Gen:  0)
   524288    524296  31856824  0.00  0.00    1.16    1.96    0    0  (Gen:  0)
   368248       808   1032992  0.00  0.02    1.16    1.99    0    0  (Gen:  1)
        0       808   1032992  0.00  0.00    1.16    1.99    0    0  (Gen:  1)
"performed!"
    39464      2200   1058952  0.00  0.00    1.16    1.99    0    0  (Gen:  1)
    22264      1560   1075992  0.00  0.00    1.16    2.00    0    0  (Gen:  0)
        0                      0.00  0.00

因此,在GC之后,堆上仍然有1M(没有-G1)。使用-G1可以看到:
 34340656  20520040  20524800  0.10  0.12    0.76    0.85    0    0  (Gen:  0)
 41697072  24917800  24922560  0.12  0.14    0.91    1.01    0    0  (Gen:  0)
 70790776       800   2081568  0.00  0.02    1.04    1.20    0    0  (Gen:  0)
        0       800   2081568  0.00  0.00    1.04    1.20    0    0  (Gen:  0)
"performed!"
    39464      2184   1058952  0.00  0.00    1.05    1.21    0    0  (Gen:  0)
    22264      2856     43784  0.00  0.00    1.05    1.21    0    0  (Gen:  0)
        0                      0.00  0.00

大约2M。这是在x86_64 / Linux上。

让我们考虑the STG machine storage model,看看堆上是否还有其他东西。

在那1M的空间中可能发生的事情:
  • CAF是否支持[],字符串常量,小的IntChar池等内容,以及库中的stdin MVar?
  • main线程的
  • Thread State Objects(TSO)。
  • 任何分配的信号处理程序。
  • IO管理器Haskell代码。
  • 火花池中的火花

  • 从经验来看,这个略低于1M的数字似乎是GHC二进制文件的默认“足迹”。这也是我在其他程序中看到的(例如,枪战程序的最小足迹永远不会小于900K)。

    探查器也许可以说些什么。这是-hT配置文件(不需要分析库),在我在末尾插入一个最小的忙循环以消除尾巴之后:
     $ ./A +RTS -K10M -S -hT -i0.001
    

    该图的结果:



    胜利!看看那里的〜1M线程堆栈对象!

    我不知道缩小TSO的方法。

    产生上图的代码:
    import Language.Haskell.Exts.Annotated -- from haskell-src-exts
    import System.Mem
    import System.IO
    import Data.Int
    import Control.Exception
    
    main :: IO ()
    main = do
      evaluate $ length $ show $ fromParseResult
               $ parseFileContents
               $ "data C = C {a :: F {- " ++ replicate 400000 'd' ++ " -}     }"
      performGC
      performGC
      print "performed!"
      performGC
    
      -- busy loop so we can sample what's left on the heap.
      let go :: Int32 -> IO ()
          go  0 = return ()
          go  n = go $! n-1
      go (maxBound :: Int32)
    

    10-07 20:19