代码之家  ›  专栏  ›  技术社区  ›  effectfully

外部数据和垃圾收集

  •  1
  • effectfully  · 技术社区  · 7 年前

    当我通过FFI分配一些数据并将终结器与之关联时,我得到 ForeignPtr 在哈斯克尔。当该指针未被引用时,GC收集该指针,从而使终结器运行。但是收集只在GC已经运行时发生,并且“取消引用”不会强制GC运行。一、 e.可能有很多指针,但由于指针本身不占用太多内存,RTS根本看不到启动GC的理由,因为RTS没有跟踪外来数据的大小,根据我的 investigations . 这是正确的吗?

    我如何与RTS沟通“当此指针未被引用时,立即收集它”?是否有允许控制何时启动GC的标志?对于一个真正的程序来说,这是一个问题吗(因为任何真正的程序总是有足够的显式垃圾来刺激GC)?

    2 回复  |  直到 7 年前
        1
  •  3
  •   András Kovács    7 年前

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

    Haskell land没有跟踪外国分配。如果您想要更多的控制,但没有自定义GC或引用计数,您可以使用例如。 Foreign.Marhsal.Array 用于手动/范围内的分配和解除分配。

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

        2
  •  1
  •   chi    7 年前

    理解指针何时变为未引用并非易事。 据我所知,没有办法执行您的请求,即通知GC现在不再可以访问指针。充其量,可以触发一个GC循环,但没有硬性保证。

    根据您的描述,您可能更喜欢引用计数机制,而不是垃圾收集。然而,特别是在复杂的纯代码中,很难确定计数器应该递增或递减的点:在基于状态或IO的单子中,如果这些副作用在计算的其余部分正确排序,则更容易确定。

    如果您真的不需要引用计数超过“1”,那么一个常见的习惯用法是使用 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 s、 其设计目的是防止指针脱离其预期范围(如上所述)。这利用了rank-2类型,但很有效。

    尽管如此,人们还是可以忍受天真 具有 常规,只是要小心不要让指针跑掉。

    另一个不安全问题是由 action 能够抛出异常。(这可以使用 bracket -如库中的例程)。