10

次のスニペットを検討してください。

l = []

while 1
  l << 'a random 369-characterish string'
end
^C
# ran this for maybe 4 seconds, and it had 27 million entries in l. memory
# usage was 1.6 GB.

l = nil

# no change in memory usage

GC.start

# memory usage drops a relatively small amount, from 1.6 GB to 1.39 GB.

私は何百万もの要素をRubyのデータ構造に/を通じて押し込んでおり、深刻なメモリの問題を抱えています. この例は、現存するオブジェクトへの参照がない場合でも、Ruby は を明示的に呼び出した後でも [ほとんど] を手放さないことを示していGC.startます。

私が実生活で使用しているオブジェクトは、合計で数百万の要素をハッシュにプッシュしますが、ハッシュは一時的なルックアップ テーブルとして使用され、ループが完了するとゼロになります。ただし、このルックアップ テーブルからのメモリは決して解放されないようです。GC にはサイクルごとに分析する必要のある何百万もの無効なオブジェクトがあるため、アプリケーションの速度が非常に遅くなります。私はsparsehashgemで回避策に取り組んでいますが、これはRubyランタイムがそのように窒息するような手に負えない問題ではないようです。参照は明確に削除され、オブジェクトは明確に収集および廃棄されるべきです。なぜこれが起こっていないのかを理解するのを手伝ってくれる人はいますか?

freenode の #ruby でユーザーの提案を試してみl.delete_if { |x| true}ましたが、これは非常に遅く、メモリのかなりの解放を引き起こすこともありませんでした。

を使用してruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]います。

編集:

比較のために、ここで実行しpython3ます:

l = []

while 1:
    l.append('a random 369-characterish string')
^C
# 31,216,082 elements; 246M memory usage.

l = []
# memory usage drops to 8K (0% of system total)

python2 でのテストでは、ほぼ同じ結果が表示されます。

これが MRI の実装上の欠陥であると考えるのに十分なのか、それとも GC へのさまざまなアプローチのせいなのかはわかりません。いずれにせよ、Python は、合計で数百万の要素をデータ構造にプッシュし、定期的に構造をゼロにする (一時的なルックアップ テーブルの場合のように) ユース ケースにより適しているようです。

これは単純なものであるべきだと本当に思われます。:\

4

1 に答える 1

1

forkちょっとハックですが、操作を別のプロセスとして試すことができます。プロセスは共有メモリ空間で実行されます。終了すると、メモリが解放されます。

@Sergio Tulentsevがコメントで指摘したように、Rubyはメモリをカーネルに解放していない可能性があります。

この Ruby/Unix メーリング リストの会話では、これについて詳しく説明しています:システム コールの回避

また、このブログ投稿では、Rails でのメモリ管理のソリューションとしてのフォークについて説明しています: Saving memory in Ruby on Rails with fork() and copy-on-write . ただし、Ruby 2 が登場するまで、Ruby はコピー オン ライトをサポートしないと思います。

于 2012-09-19T14:46:28.010 に答える