国外数据和垃圾收集

问题描述 投票:1回答:2

当我通过FFI分配一些数据并将终结器与它关联时,我在Haskell中获得了一个ForeignPtr。当此指针变为未引用时,GC会收集指针,从而导致终结器运行。但收集仅在GC运行且“不引用”不强制GC运行时发生。即可能会有很多指针,但由于指针本身不会占用太多内存,因此RTS根本没有看到启动GC的原因,因为根据我的investigations,RTS不会跟踪外部数据的大小。它是否正确?

如何“当指针变为未引用时,立即将其收集”到RTS?是否有任何标志可以控制何时启动GC?这是一个真正的程序的问题(因为任何真正的程序总是有足够的显性垃圾刺激GC)?

haskell garbage-collection ffi
2个回答
3
投票

在运行GC之前,RTS不知道是否有任何数据是未引用的。 GHC没有引用计数GC,这将允许立即对垃圾采取行动。您可以尝试自己实现引用计数,或使用System.Mem中的手动GC。

在Haskell-land中没有跟踪外部分配。如果您想要更多控制,但没有自定义GC或引用计数,则可以使用e。 G。 Foreign.Marhsal.Array用于手动/范围分配和释放。

另一种选择是在GHC RTS中使用固定分配。这为您提供了不被GC移动的内存。对固定数据的引用可以在没有开销的情况下传递给外部代码,但是跟踪固定数据,可以是GC-d,并且像通常的堆数据一样触发GC。 Here's用于固定数据的API。另一个选择就是ByteString。固定数据的可能缺点是内存碎片和较慢的分配,但这也适用于(任何)返回稳定指针的外部分配。


1
投票

了解何时指针变为未引用并非易事。据我所知,无法执行您的请求,即通知GC现在无法再访问指针。最好的情况是,可以触发GC循环,但没有硬性保证。

根据您的描述,您可能更喜欢引用计数机制而不是垃圾收集。但是,特别是在复杂的纯代码中,很难识别计数器应该递增或递减的点:在基于状态或基于IO的monad中,如果这样的副作用被正确排序,则会更容易w.r.t.其余的计算。

如果你真的不需要超过“one”的引用计数,那么一种常见的习惯用法是使用with风格的函数来处理分配和释放。这可能有点难以正确处理。

例如,一个简单的实现可能是

-- very simplified code
withMyResource :: (ResourcePtr -> IO r) -> IO r
withMyResource action = do
   p <- allocResourcePtr
   result <- action p
   deallocResourcePtr p
   return result

然后可以将其用作

withResource $ \ptr -> do
   use ptr

请注意,这不是完全安全的,因为可能会返回指针,使其在释放后生效

ptr <- withResource return
use ptr -- dangerous!

一个正确的指针处理例程应该像ST monad及其标记的STRefs一样工作,它们的设计是为了防止指针逃避它们的预期范围(如上所述)。这会利用rank-2类型,但是有效。

仍然,人们可以忍受天真的with例程,并且小心不要让指针逃脱。

另一个不安全的问题是action能够抛出异常引起的。 (这可以使用库中的类似bracket的例程来处理)。

© www.soinside.com 2019 - 2024. All rights reserved.