为什么performGC无法释放所有内存?

Nei*_*ell 19 garbage-collection haskell ghc

鉴于该计划:

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
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

performGC调用似乎留下了8Mb的内存,即使看起来所有的内存都应该死了.怎么会?

(没有-G1我在最后看到10Mb直播,我也无法解释.)

Don*_*art 18

这就是我所看到的(在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
Run Code Online (Sandbox Code Playgroud)

所以在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
Run Code Online (Sandbox Code Playgroud)

所以大约2M.这是在x86_64/Linux上.

让我们考虑一下STG机器存储模型,看看堆上是否有其他东西.

可能存在于1M的空间中的事情:

  • CAF用于诸如[]字符串常量,小型IntChar池,以及库中的东西,stdinMVar?
  • 线程的线程状态对象(TSO)main.
  • 任何分配的信号处理程序.
  • IO管理器Haskell代码.
  • 火花池中的火花

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

也许剖析器可以说些什么.这是-hT配置文件(不需要配置文件库),我在最后插入一个最小的忙循环以排出尾部:

 $ ./A +RTS -K10M -S -hT -i0.001    
Run Code Online (Sandbox Code Playgroud)

此图表中的结果:


在此输入图像描述


胜利!看看坐在那里的~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)
Run Code Online (Sandbox Code Playgroud)

  • 正确,虽然我不确定为什么尼尔看到8M直播.1M是我对7.0.3的期望,因为RTS会释放多余的堆栈空间,直到达到1M.请注意,使用7.2.1,您将看到该数字下降到32k,因为堆栈现在以32k块分配. (9认同)
  • PS相同的图表在`hp2pretty` http://i.imgur.com/P0F1N.png(ezyang的新工具)下. (2认同)
  • (仅供参考:我没有写hp2pretty :-) (2认同)