Ответ 1
Вот что я вижу (после вставки 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
Итак, после GCs все еще 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.
Подумайте о модели хранения машин STG, чтобы узнать, есть ли что-то еще в куче.
Вещи, которые могут быть в этом 1M пространства:
- CAF для таких вещей, как
[]
, строковые константы и небольшой пулInt
иChar
, а также вещи в библиотеках,stdin
MVar? - Объекты состояния потока (TSOs) для потока
main
. - Любые выделенные обработчики сигналов.
- Код Haskell менеджера IO.
- Искры в искровом пуле
Из опыта эта цифра чуть меньше 1 М кажется по умолчанию "следностью" двоичного кода GHC. Это о том, что я видел и в других программах (например, программа перестрелки с наименьшими отпечатками не менее 900K).
Возможно, профайлер может что-то сказать. Здесь профиль -hT
(без необходимости профилирования libs), после того, как я вставляю в конец минимальный цикл занятости, чтобы вывести хвост:
$ ./A +RTS -K10M -S -hT -i0.001
Результаты на этом графике:
Победа! Посмотрите на этот объект стека ~ 1M, сидящий там!
Я не знаю, как уменьшить TSOs.
Код, создавший приведенный выше график:
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 left on the heap.
let go :: Int32 -> IO ()
go 0 = return ()
go n = go $! n-1
go (maxBound :: Int32)