当我通过FFI分配一些数据并将终结器与其关联时,我在Haskell中得到了一个ForeignPtr。当该指针变为未引用时,GC将收集该指针,这会导致终结器运行。但收集仅在GC运行时发生,并且“取消引用”不会强制GC运行。也就是说,可以有许多指针,但由于指针本身不占用太多内存,RTS根本看不到启动GC的理由,因为根据我的investigations,RTS不会跟踪外部数据的大小。这是正确的吗?
我如何向RTS传达“当此指针变为未引用时,立即收集它”?是否有允许控制何时启动GC的标志?这对于真正的程序来说是个问题吗(因为任何真正的程序总是有足够的显式垃圾来刺激GC)?
发布于 2017-12-30 05:37:36
在运行GC之前,RTS不知道是否有任何数据未被引用。GHC没有引用计数GC,这将允许对垃圾立即采取行动。您可以尝试自己实现引用计数,或者使用System.Mem中的手动GC。
在Haskell-land中不跟踪国外分配。如果您想要更多控制,但没有自定义GC或引用计数,则可以使用Foreign.Marhsal.Array进行手动/作用域分配和释放。
另一种选择是在GHC RTS中使用固定分配。这为您提供了不被GC移动的内存。对固定数据的引用可以在没有开销的情况下传递给外部代码,但是固定数据是被跟踪的,可以是GC-d,并且触发GC的方式与通常的堆数据相同。用于固定数据的Here's one API。另一种选择是简单的ByteString。固定数据的可能缺点是内存碎片和较慢的分配,但这也适用于(任何)返回稳定指针的外部分配。
发布于 2017-12-30 05:30:02
理解指针何时变得未被引用并不是一件容易的事。据我所知,没有办法执行您的请求,即通知GC指针现在不再可达。充其量,一个人可以触发GC周期,但没有硬保证。
根据您的描述,您可能更喜欢引用计数机制,而不是垃圾收集。然而,特别是在复杂的纯代码中,很难识别计数器应该递增或递减的点:在状态或基于IO的monad中,这将更容易,其中这些副作用被适当地排序w.r.t.计算的其余部分。
如果你真的不需要“1”以外的引用计数,一种常见的习惯用法是使用with-style函数来处理分配和释放。这可能会变得有点棘手,无法正确处理。
例如,一个简单的实现可能是
-- 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-like例程来处理)。
https://stackoverflow.com/questions/48028503
复制相似问题