Подтвердить что ты не робот

Как я могу освободить память GHCI

Введение

Следующий код показывает, что при использовании runhaskell Haskell Garbage Collector освобождает память, когда a больше не используется. Это приводит к дампу ядра при выпуске переменной a - для определенной цели, для проверки поведения - a получил nullFunPtr в качестве финализатора.

module Main where

import Foreign.Ptr 
import Foreign.ForeignPtr


main :: IO ()
main = do
    a <- newForeignPtr nullFunPtr nullPtr
    putStrLn "Hello World"

Проблема

При запуске в ghci он не освобождает память. Как заставить ghci освобождать больше не используемые переменные?

$ ghci
> import Foreign.Ptr
> import Foreign.ForeignPtr
> import System.Mem
> a <- newForeignPtr nullFunPtr nullPtr
> a <- return () -- rebinding variable a to show gc that I'm no longer using it
> performGC
> -- did not crash - GC didn't release memory
> ^D
Leaving GHCi.
[1]    4396 segmentation fault (core dumped)  ghci

Память была выпущена при выходе, но для меня это слишком поздно. Я расширяю GHCi и использую его для других целей, и мне нужно освободить память раньше - по требованию или как можно быстрее, будет действительно здорово.

Я знаю, что я могу вызвать finalizeForeignPtr, но я использую foreignPtr только для целей отладки. Как я могу освободить a вообще в последнем примере?

Если нет возможности сделать это с помощью ghci prompt, я также могу изменить код ghci. Может быть, я могу освободить этот a с помощью modyfing интерактивного контекста ghci или DynFlags? До сих пор мне не повезло с моим reaserch.

4b9b3361

Ответ 1

Прослеживая код, мы обнаруживаем, что это значение сохраняется в поле closure_env типа данных PersistentLinkerState, которое является ClosureEnv, то есть отображением от имени до HValue s. Соответствующей функцией в Linker.hs является

extendLinkEnv :: [(Name,HValue)] -> IO ()
-- Automatically discards shadowed bindings
extendLinkEnv new_bindings =
  modifyPLS_ $ \pls ->
    let new_closure_env = extendClosureEnv (closure_env pls) new_bindings
    in return pls{ closure_env = new_closure_env }

и хотя комментарий указывает, что он должен удалить привязанную привязку, он, по крайней мере, не так, как вы хотите.

Причина в том, что, поскольку AndrewC пишет правильно: хотя обе переменные имеют одинаковое исходное кодовое имя, они отличаются от компилятора (у них есть другой Unique). Мы можем наблюдать это после добавления некоторой трассировки к указанной выше функции:

*GHCiGC> a <- newForeignPtr nullFunPtr nullPtr
extendLinkEnv [a_azp]
*GHCiGC> a <- return ()
extendLinkEnv [a_aF0]
*GHCiGC> performGC
extendLinkEnv [it_aFL]

Удаление привязок с тем же именем источника на этом этапе должно решить вашу проблему с GC, но я не знаю, компилятор достаточно хорошо, чтобы сказать, что еще сломает. Я предлагаю вам открыть билет, надеюсь, кто-то узнает.

Путаница при привязке к значению

В комментариях, похоже, возникает некоторая путаница в отношении привязок и значений. Рассмотрим этот код:

> a <- return something
> b <- return somethingelse
> a <- return (b+b)
> b <- return anewthing

В текущей реализации куча будет состоять из `

  • something
  • somethingelse
  • a thunk, ссылающийся на оператор (+) и somethingelse
  • anewthing.

Кроме того, среда интерпретатора имеет ссылки на все четыре значения кучи, поэтому ничто не может быть GCed.

Правильно ожидаемое remdezx заключается в том, что GHCi отбросит ссылку на something и somethingelse. Это, в свою очередь, позволит системе времени выполнения собирать мусор something (мы не предполагаем дальнейших ссылок). GHCi все еще ссылается на thunk, который, в свою очередь, ссылается на somethingelse, поэтому это не будет собирать мусор.

Ясно, что вопрос был очень специфичным для реализации, и вот такой ответ: -)