如何释放 Haskell 中特定数据结构的内存?

How to free memory of a specific data structure in Haskell?

假设我有几个非常大的向量。它们存储在磁盘上。我需要通过读取每个将它们放入内存的文件来单独访问它们。我会在单个向量上执行一些功能,然后转到下一个我需要访问的向量。每次我需要访问不同的向量时,我都需要能够指示内存中的每个向量被垃圾收集。我不确定 performMajorGC 是否会确保如果在我的程序中声明我必须稍后通过引用从磁盘读取向量的相同函数名称再次访问相同的向量,那么向量将被垃圾收集.在这种情况下,我会再次将它读入内存,使用它,然后对它进行垃圾回收。我如何确保它是车库集合,同时对从同一文件读取的向量使用相同的函数名称?

如有任何建议,将不胜感激

回复丹尼尔·瓦格纳:

    myvec x :: Int -> IO (Vector (Vector ByteString))
    myvec x = do let ioy = do y <- Data.ByteString.Lazy.readFile ("data.csv" ++ (show x))
                              guard (isRight (Data.Csv.decode NoHeader y)) 
                              return y
                 yy <- ioy 
                 return (head $ snd $ partitionEithers [Data.Csv.decode NoHeader yy])

    myvecvec :: Vector (IO (Vector (Vector ByteString)))
    myvecvec = generate 100 (\x -> myvec x)

    somefunc1 :: IO (Vector (Vector ByteString)) -> IO ()
    somefunc1 iovv = do vv <- iovv
                        somefunc1x1 vv :: Vector (Vector ByteString) -> IO ()  

-- somefunc2 和 3 也一样

    oponvec :: IO ()
    oponvec = do somefunc1 (myvecvec ! 0)
                 performGC
                 somefunc2 (myvecvec ! 1)
                 performGC
                 somefunc3 (myvecvec ! 0)
    

您可以使用弱指针进行测试,如下所示:

import qualified Data.Vector.Unboxed as V
import System.Mem.Weak
import System.Mem

main :: IO ()
main = do
  let xs = V.fromList [1..1000000:: Int]
  wkp <- mkWeakPtr xs Nothing
  performGC
  xs' <- deRefWeak wkp
  print xs'

在我的系统上,这会打印 Nothing,这意味着向量已被释放。但是,我不知道 GHC 是否保证会发生这种情况。

这是一个检查@amalloy 的建议的程序:

import qualified Data.Vector.Unboxed as V
import Control.Monad
import Data.Word

{-# NOINLINE newLarge #-}
newLarge :: Word8 -> V.Vector Word8
newLarge n = V.replicate 5000000000 n -- 5GB

main :: IO ()
main = forM_ [1..10] $ \i -> print (V.sum (newLarge i))

这在我的机器上恰好使用了 5GB,这表明绝不会同时分配两个大向量。

I need to be able to instruct each vector in memory to be garbage collected every time I need to access a different vector.

你呢?为什么?如果只是因为它们很大而您担心在内存中拟合向量,那么请不要担心。如果需要内存 space,并且对象不可访问,那么垃圾收集器将拾取它。如果不需要内存 space,则无需执行任何操作。如果对象是可达的,运行 GC 将无济于事。因此,没有任何情况下手动干预 GC 会有任何好处。

如果您出于释放内存以外的其他原因想要对其进行 GC,则需要在问题中进行解释,因为该目标肯定会影响答案。