Освобождение памяти, выделенной с помощью newCString

Как говорят документы библиотеки CString, созданные с помощью newCString, должны быть освобождены с помощью функции free. Я ожидал, что при создании CString потребуется немного памяти, и когда он будет выпущен с использованием free, использование памяти снизится, но это не так! Вот пример кода:

module Main where

import Foreign
import Foreign.C.String
import System.IO

wait = do
  putStr "Press enter" >> hFlush stdout
  _ <- getLine
  return ()

main = do
  let s = concat $ replicate 1000000 ['0'..'9']
  cs <- newCString s
  cs `seq` wait   -- (1)

  free cs
  wait   -- (2)

Когда программа остановилась на (1), программа htop показала, что использование памяти составляет около 410M - это нормально. Я нажимаю enter, и программа останавливается в строке (2), но использование памяти по-прежнему составляет 410M, несмотря на то, что cs был free d!

Как это возможно? Подобная программа, написанная на C, ведет себя так, как должна. Что мне здесь не хватает?

Ответ 1

Проблема в том, что free просто указывает сборщику мусора, что теперь он может собирать строку. На самом деле это не заставляет сборщик мусора работать - это просто указывает, что CString теперь мусор. По-прежнему ГК решает, когда запускать, исходя из эвристики давления кучи.

Вы можете принудительно создать основную коллекцию, вызвав performGC прямо после вызова free, что сразу уменьшит память до 5M или около того.

например. эта программа:

import Foreign
import Foreign.C.String
import System.IO
import System.Mem

wait = do
  putStr "Press enter" >> hFlush stdout
  _ <- getLine
  return ()

main = do
  let s = concat $ replicate 1000000 ['0'..'9']
  cs <- newCString s
  cs `seq` wait   -- (1)

  free cs
  performGC
  wait   -- (2)

Ведет себя так, как ожидалось, со следующим профилем памяти - первая красная точка - это вызов performGC, немедленно освобождающий строку. Затем программа останавливается примерно на 5 м до тех пор, пока не прекратится.

enter image description here