ruby 关于从方法返回的超出范围的FFI MemoryPointer的问题

ejk8hzay  于 2023-06-22  发布在  Ruby
关注(0)|答案(1)|浏览(71)

假设我有以下使用FFI库的ruby代码:

class SimpleStruct < FFI::Struct
    layout :value, :pointer
  end
  
  class Test1
    def self.foo(s)
      result = FFI::MemoryPointer.from_string(s)

      result
    end

    def self.bar(s)
      simple_struct = SimpleStruct.new
      value = FFI::MemoryPointer.from_string(s)
      simple_struct[:value] = value

      simple_struct
    end
  end
  
  
  class Test2
    def self.testing
      a = Test1.foo('test')
      b = Test1.bar('test')

      puts a.read_string, b[:value].read_string
    end
  end

FFI wiki提到了When a MemoryPointer goes out of scope, the memory is freed up as part of the garbage collection process。在上面的Test1类中,foo方法返回MemoryPointer,bar方法返回保存MemoryPointer的FFI结构体。Test2类中的testing方法调用这些方法,并将返回值分别存储在变量ab中。
我的问题是,在这种情况下,在foobar方法中创建的MemoryPointer何时会被垃圾收集?是当MemoryPointer变量在foobar方法中超出作用域时,还是当局部变量ab(引用从foo/bar方法返回的MemoryPointer)在testing方法中超出作用域时?

az31mfrm

az31mfrm1#

我相信FFI::Struct#[]=会处理这个问题。我没有在源代码中检查它,但我在代码中添加了一些检查,看起来是这样的。

require 'ffi'

class SimpleStruct < FFI::Struct
  layout :value, :pointer
end

class Test1
  def self.foo(s)
    puts "foo is called"
    result = FFI::MemoryPointer.from_string(s)
    ObjectSpace.define_finalizer(result, proc { puts "foo result is garbage collected" })
    result
  end

  def self.bar(s)
    puts "bar is called"
    simple_struct = SimpleStruct.new
    ObjectSpace.define_finalizer(s, proc { puts "bar result is garbage collected" })
    value = FFI::MemoryPointer.from_string(s)
    ObjectSpace.define_finalizer(s, proc { puts "bar result[:value] is garbage collected" })
    simple_struct[:value] = value

    simple_struct
  end

  def self.baz(s)
    puts "baz is called"
    simple_struct = {}
    ObjectSpace.define_finalizer(s, proc { puts "baz result is garbage collected" })
    value = FFI::MemoryPointer.from_string(s)
    ObjectSpace.define_finalizer(s, proc { puts "baz result[:value] is garbage collected" })
    simple_struct[:value] = value

    simple_struct
  end
end

class Test2
  def self.testing
    puts "testing is started"
    a = Test1.foo('foo')
    b = Test1.bar('bar')
    c = Test1.baz('baz')

    puts a.read_string, b[:value].read_string, c[:value].read_string
    puts "testing is finished"
  end
end

GC.stress

Test2.testing

# testing is started
# foo is called
# bar is called
# baz is called
# foo
# bar
# baz
# testing is finished
# baz result is garbage collected
# baz result[:value] is garbage collected
# bar result is garbage collected
# bar result[:value] is garbage collected
# foo result is garbage collected

指针只有在结构体被垃圾回收时才被垃圾回收,就像散列一样。

相关问题