haskell - ¿Cómo puedo hacer que GHCI libere memoria?
memory-management garbage-collection (1)
La introducción
El siguiente código muestra que al usar runhaskell
Haskell Garbage Collector libera la memoria, cuando a
ya no se usa. Resulta en el volcado del núcleo mientras se libera la variable a
, para un propósito, para inspeccionar el comportamiento, a
tiene nullFunPtr
como finalizador.
module Main where
import Foreign.Ptr
import Foreign.ForeignPtr
main :: IO ()
main = do
a <- newForeignPtr nullFunPtr nullPtr
putStrLn "Hello World"
El problema
Al ejecutar lo mismo en ghci, no libera memoria. ¿Cómo puedo forzar a ghci a liberar variables ya no usadas?
$ 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
La memoria se lanzó al salir, pero ya es demasiado tarde para mí. Estoy extendiendo GHCi y usándolo para otro propósito y necesito liberar la memoria antes, a pedido o tan rápido como sea posible sería realmente genial.
Sé que puedo llamar a finalizeForeignPtr
, pero estoy usando foreignPtr
solo para depuración. ¿Cómo puedo liberar a
en general en el último ejemplo?
Si no hay posibilidad de hacerlo con el indicador ghci, también puedo modificar el código ghci
. Tal vez pueda lanzar esto mediante modyfing ghci Interactive Context o DynFlags ? Hasta ahora no he tenido suerte con mi reaserch.
Rastreando a través del código encontramos que el valor se almacena en el campo closure_env
del tipo de datos PersistentLinkerState
, que es un ClosureEnv
, es decir, un mapeo de nombre a HValue
s. La función relevante en Linker.hs
es
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 }
y aunque el comentario indica que debe eliminar el enlace oculto, no lo hace, al menos no de la manera que usted desea.
La razón es que, como AndrewC escribe correctamente: aunque ambas variables tienen el mismo nombre de código fuente, son diferentes al compilador (tienen un Unique
adjunto). Podemos observar esto luego de agregar algún rastreo a la función anterior:
*GHCiGC> a <- newForeignPtr nullFunPtr nullPtr
extendLinkEnv [a_azp]
*GHCiGC> a <- return ()
extendLinkEnv [a_aF0]
*GHCiGC> performGC
extendLinkEnv [it_aFL]
Eliminar enlaces con el mismo nombre-fuente en este punto debería resolver su problema GC, pero no conozco el compilador lo suficiente como para decir qué más podría romperse. Le sugiero que abra un boleto, con suerte alguien lo sabrá.
Confusión en unión vs. valor
En los comentarios, parece haber cierta confusión sobre enlaces y valores. Considera este código:
> a <- return something
> b <- return somethingelse
> a <- return (b+b)
> b <- return anewthing
Con la implementación actual, el montón consistirá en `
-
something
-
somethingelse
- un thunk haciendo referencia al operador
(+)
ysomethingelse
malo -
anewthing
Además, el entorno del intérprete tiene referencias a los cuatro valores del montón, por lo que no se puede hacer GC.
Lo que remdezx correctamente esperaba es que GHCi soltara la referencia a something
y somethingelse
. Esto, a su vez, permitiría que el sistema de tiempo de ejecución recoja something
(no asumimos más referencias). GHCi todavía hace referencia al thunk, que a su vez hace referencia a somethingelse
así, por lo que no sería basura.
Claramente, la pregunta fue muy específica para la implementación, y esta es la respuesta :-)