为什么SPEGC不能释放所有内存?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (12)

鉴于该方案:

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的内存,尽管似乎所有的内存都应该死掉了。

提问于
用户回答回答于

插入一个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

大约200万。这是在x86上_64/Linux。

看看堆里有没有别的东西。

可能存在于那100万空间里的东西:

  • 的CAF[],字符串常量,和小IntChar池,再加上库中的内容,stdinMVar?
  • main线。
  • 任何分配的信号处理程序。
  • IO管理器Haskell代码。
  • 火花池

根据经验,这个略小于100万的数字似乎是GHC二进制文件的默认“占用”。这也是我在其他程序中看到的(例如,枪战程序,最小的足迹从来不少于900 K)。

也许分析人员可以说些什么。这是-hT配置文件(不需要配置文件库),在后面插入一个最小的繁忙循环,将尾串出来:

 $ ./A +RTS -K10M -S -hT -i0.001    

图中的结果:

生成上述图形的代码:

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)
用户回答回答于

编译代码-O -ddump-simpl,我在简化器输出中看到以下全局定义:

lvl2_r12F :: [GHC.Types.Char]
[GblId]
lvl2_r12F =
  GHC.Base.unpackAppendCString# "data C = C {a :: F {- " lvl1_r12D

解析器函数的输入已成为全局字符串常量。全局从不会在GHC中被垃圾收集,所以这可能是垃圾收集之后占用8MB内存的原因。

扫码关注云+社区