5

Ruby ファイナライザーをいじっていて、私にとって非常に奇妙な動作に気付きました。トリガー コードを次のように減らすことができます。

require "weakref"

class Foo
    def initialize
        ObjectSpace.define_finalizer(self, self.class.finalize)
    end

    def self.finalize
        proc {
            puts "finalizing"
        }
    end
end

Foo.new # does not work
#WeakRef.new(foo) # Using this instead, everything works as expected
sleep 1
ObjectSpace.garbage_collect
puts "... this did not finalize the object"

Foo.new
ObjectSpace.garbage_collect
puts "but this did?"

プログラムが示すように、Foo.new への 2 回目の呼び出しの前にファイナライザーは実行されません。ガベージ コレクターへの最初の呼び出しの前にさらに遅延を追加しようとしましたが (ただし、私が理解しているように、まったく必要ないはずです)、何もしません。

奇妙なことに、コメントアウトされた行 i を使用すると、期待どおりに最初のファイナライザーが呼び出されます。2番目のものは、プログラムが終了する前にまだ呼び出されていません。

なぜこれが起こっているのか誰でも説明できますか?Ruby 1.9.3p194 (2012-04-20 リビジョン 35410) [x86_64-linux] で Ubuntu 12.10 を実行しています。私はweakrefコードを読んでみましたが、私が知る限り、オブジェクトobject_idを保存して後で取得するだけです。

編集:このような状況でガベージコレクターを手動で呼び出すことは意味がないことを理解しています。この背後にあるメカニズムを理解しようとしているだけです。

4

3 に答える 3

3

Fooファイナライザーで参照されているため、参照を収集できません! したがって、ファイナライザー自体がオブジェクトへの参照を保持しているため、GC はそれを収集せず、ファイナライザーをトリガーしません。ファイナライザー自体に WeakRef を使用するだけで、これを回避できます。

require "weakref"

class Foo
  class << self
    attr_accessor :objects_finalized

    def finalize
      proc {
        @objects_finalized ||= 0
        @objects_finalized += 1
      }
    end
  end

  def initialize
    ObjectSpace.define_finalizer WeakRef.new(self), self.class.finalize
  end
end

describe Foo do
  it "should be collected" do
    Foo.new
    expect { GC.start }.to change {
      ObjectSpace.each_object(Foo){} }.from(1).to(0)
  end

  it "should be finalized when it is collected" do
    expect { begin; Foo.new; end; GC.start }.to change {
      Foo.objects_finalized }.from(nil).to(1)
  end
end

結果:

% rspec weakref.rb
..

Finished in 0.03322 seconds
2 examples, 0 failures
于 2012-12-29T23:52:25.680 に答える
1

http://edwinmeyer.com/Release_Integrated_RHG_09_10_2008/chapter05.htmlで答えを見つけました(「Registers and the Stack」を検索してください)

オブジェクトへの参照は依然としてプロセッサ レジスタに格納されているため、ガベージ コレクタは安全を保ち、オブジェクトがまだ有効であると想定します。

于 2012-12-29T23:29:20.447 に答える
0

Objective-C や C++ などのオブジェクトへのすべての参照がなくなるとすぐに消える言語とは異なり、Ruby はガベージ コレクション言語です。インタープリターには、1 つのオブジェクトに対してかさばる非効率的なガベージ コレクターを呼び出す理由はありません。ガベージ コレクタが実行されると、他のすべての処理が停止します。これは大きなパフォーマンス ヒットです。インタプリタは、収集する前にほとんどのガベージがなくなるまで待機するのに十分スマートです。

例:ゴミ1個が入ったゴミ袋をゴミ箱に持って行きますか?いいえ、いっぱいになるまで待ってから行きます。

GC コレクションを強制する場合はGC.garbage_collect、コレクターを手動で呼び出してみてください。ただし、よほどの理由がない限り、これを本番環境で使用しないでください。

于 2012-12-28T04:28:03.200 に答える