K.Sasada's Home Page

こめんとのついか

こめんとこめんと!

message

please add long comment :).

_8(Sat)

Ruby VM アドベントカレンダーの 8 日目です.

そろそろ息切れしてきました.

今日は,

で紹介されている ObjectSpace.reachable_objects_from(obj) をネタにしてみます.なお,このメソッドは require 'objspace' として,objspace ライブラリを require しないと使えません.

ObjectSpace.reachable_objects_from(obj) は,obj から直接辿ることができるオブジェクトをすべて列挙して返します.

次に例を示します.

(1) は,問題無いと思いますが,(2),(3) の違いはわかるでしょうか.同じオブジェクトを複数回辿ることが出来るとき,1つしか返さないようになっています.

さて,これを使って,obj から辿ることができる,すべてのオブジェクトが必要とするメモリサイズを計算するコードを書いてみます.「直接たどることができる」ではなく,「辿ることができる全てのオブジェクト」というところに注意して下さい.つまり,再帰的に ObjectSpace.reachable_objects_from(obj) を適用しよう,というわけです.

例えば,[['a' * 100, 'b' * 100], 'c' * 100] という配列は,長さが 100 の文字列オブジェクト 3 つ辿れるので,合計 300 byte と出て欲しいわけです(厳密には,Array クラスオブジェクトも辿れるのですが,これは計算が面倒なので除外することにします).

あるオブジェクトが消費するメモリサイズを得るには,ObjectSpace.memsize_of(obj) というメソッドを利用します.これは,1.9 の時から入っています.

早速ですが,コードです.

require 'objspace'
require 'pp'

def memsize_of_all_reachable_objects_from(obj, exclude_class = Module)
  objs = {}
  queue = [obj]
  while obj = queue.pop
    next if objs[obj.object_id]
    next unless reachable_objects = ObjectSpace.reachable_objects_from(obj)
    reachable_objects.each{|o|
      case o
      when ObjectSpace::InternalObjectWrapper
        next if objs[o.internal_object_id]
      else
        next if objs[o.object_id]
      end
      queue.push o if !exclude_class || !o.kind_of?(exclude_class)
    }
    objs[obj.respond_to?(:internal_object_id) ? obj.internal_object_id : obj.object_id] = obj
  end
  objs.inject(0){|r, (_, o)| r += ObjectSpace.memsize_of(o)}
end

memsize_of_all_reachable_objects_from(obj, klass=Module) は,obj から辿れるすべてのオブジェクトのサイズを返します.ただし,第二引数 klass で指定されたクラスのオブジェクトは除外します.デフォルトでは Module クラスのオブジェクト,つまり Array などは辿らないようにしてみます.nil とすることで除外しないようになります.

では,やってみましょう.

p memsize_of_all_reachable_objects_from([['a' * 100, 'b' * 100], 'c' * 100])
#=> 300

ちゃんと 300 が帰ってきました.

せっかくなので,Array も含めてサイズを取ってみます.

p memsize_of_all_reachable_objects_from([['a' * 100, 'b' * 100], 'c' * 100], nil)
#=> 639428

えらい数が帰ってきました.Array クラスから辿れるオブジェクトは,凄い数にのぼり,その合計サイズも大きい,ということになります(途中にある objs のエントリ数を見てみるとわかるのですが,辿ることができるすべてのオブジェクトは 5604 個だそうです.ただし,環境による可能性があります).

memsize_of_all_reachable_objects_from() は,比較的簡単なメソッドですが,ObjectSpace::InternalObjectWrapper というものを使っていることに注意が必要です.これについては,また次の機会にご紹介します(としてネタをつなげる).興味がある方は ext/objspace/objspace.c を読んでみて下さい.

では,今日はこの辺で.


この話は RubyConf Taiwan 2012 のために書いていたコードでした.

資料:http://www.atdot.net/~ko1/activities/rubyconf.tw2012_ko1.pdf

資料には,図付きで説明があります.参考にして下さい.


好きなだけ長いコメントをどうぞ。

お名前


back

tton 記述が使えます。YukiWikiな記述してりゃ問題ありません。

「行頭に#code」 と、「行頭に#end」 で挟むと、その間の行は pre で囲まれます。プログラムのソースを書くときに使ってください。

例:

#code

(なんかプログラム書く)

#end

リンクは

[[なまえ|http://www.example.org]]

とか

[[http://www.example.org]]

で貼れます。

$Date: 2003/04/28 10:27:51 $