当我通过FFI分配一些数据并将终结器与它关联时,我在Haskell中得到attoptions
。当此指针变为未引用时,GC会收集指针,从而导致终结器运行。但是收集只发生在GC运行和"未引用"不会强制GC运行。即可能会有很多指针,但由于指针本身并没有占用太多内存,因此RTS根本没有看到启动GC的原因,因为根据我的{{{},RTS不会跟踪外来数据的大小。 3}}。这是对的吗?
如何沟通"当此指针变为未引用时,立即收集它"到RTS?是否有任何标志可以控制何时启动GC?这是一个真正的程序的问题(因为任何真正的程序总是有足够的显式垃圾来刺激GC)?
答案 0 :(得分: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 :(得分: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及其标记的STRef
一样工作,它们的设计是为了防止指针逃离预期的范围(如上所述)。这会利用rank-2类型,但是有效。
但是,人们可以忍受天真的with
例程,并且小心不要让指针逃脱。
另一个不安全的问题是action
能够抛出异常引起的。 (这可以使用bracket
来处理 - 就像库中的例程一样。)