Rubyアプリケーションの内部で何が起きている?ObjectSpaceモジュールの使い方
Rubyアプリケーションの内部で今何が起きているのか、気になったことはありませんか?
残念ながら、手軽に使える高機能なGUIツールはほとんどありません。
しかし大丈夫、RubyにはObjectSpaceモジュールがあります!
ObjectSpaceを使えば、アプリケーションの現在の状態に関する情報を取得できます。
さっそく、その仕組みを見ていきましょう。
オブジェクトの数を数える
ObjectSpaceを使えば、プログラム内で現在有効(alive)なオブジェクトを把握できます。
「オブジェクトが有効」とはどういう意味でしょうか?
オブジェクトは、どこかから参照されている限り有効です。参照とは、変数や定数のようにオブジェクトへアクセスするための手段のことです。
どこからも到達できないオブジェクトは、メモリから安全に解放できる状態にあると判断できます。
例:
# 変数 'name' が文字列 'Dave' への参照を保持している name = 'Dave' # 変数 'name' は 'John' を指すようになる # 'Dave' はもうどこからも参照されていない name = 'John'
次に、ObjectSpaceの実際の動作例を見てみましょう。
require 'objspace'
# 通常のRubyスクリプトとして有効な構文(irb/pryでは動作しません)
ObjectSpace
.each_object
.inject(Hash.new 0) { |h,o| h[o.class] += 1; h }
.sort_by { |k,v| -v }
.take(10)
.each { |klass, count| puts "#{count.to_s.ljust(10)} #{klass}" }
# コピー&ペースト版(irb/pryで使う場合はこちら)
ObjectSpace.each_object.inject(Hash.new 0) { |h,o| h[o.class] += 1; h }.sort_by { |k,v| -v }.take(10).each { |klass, count| puts "#{count.to_s.ljust(10)} #{klass}" }
これを実行すると、上位10クラスのオブジェクト数を表形式で出力します。
個数 クラス ------------------------- 5436 String 315 Class 251 Array 101 Encoding 69 Regexp 45 Hash 26 Module 25 Gem::Version 22 Gem::StubSpecification::StubLine 22 Gem::StubSpecification
メモリリークが疑われる場合は、このデータを1時間ごとに記録してみましょう。増え続けて一向に減らないオブジェクトが見つかれば、リークの原因を特定できるかもしれません。
オブジェクトを自由に操作する
ObjectSpaceでは、オブジェクトに関する情報だけでなく、実際のオブジェクトそのものにアクセスできます。そのため、「すべての文字列の値を出力する」「Fileオブジェクトのパスを一覧表示する」といった面白いことも可能です。
例:
ObjectSpace
.each_object(String)
.sort_by { |s| s.size }
.each { |s| p s }
これを実行すると、メモリ上のすべての文字列がサイズ順に出力されます。自分で作成した覚えのない文字列が大量にあることに気づくでしょう。それらはRubyインタプリタが内部的に生成したものです。
実用的な用途は?
正直なところ、主な用途はデバッグとアプリの統計情報の収集です 🙂
オブジェクトのメモリサイズを調べる
ObjectSpace.memsize_ofを使えば、特定のオブジェクトが占めるメモリサイズを調べることもできます。
例:
o = "a" * 100 ObjectSpace.memsize_of(o)
ただし、ドキュメントには次のような注意書きがあります。
「返されるサイズは不完全なものです。この情報はあくまでヒントとして扱ってください。」
さまざまな種類のオブジェクトでこのメソッドを試すと興味深い発見があります。たとえば、Fixnumは常に0を返します。
ObjectSpace.memsize_of(42) # => 0
これは、Rubyが内部的にFixnumオブジェクトを生成しないためです。詳しくは、Rubyの数値について書いた記事をご覧ください。(※FixnumはRuby 2.4以降、Integerに統合されました。)
もうひとつ興味深いのが文字列です。
ObjectSpace.memsize_of("A" * 22)
# => 40
ObjectSpace.memsize_of("A" * 23)
# => 40
ObjectSpace.memsize_of("A" * 24)
# => 65
長い文字列をいちいち入力せずに済むよう、
"A" * サイズという書き方を使っています 🙂
おっと、ここで何が起きたのでしょう?
実は、Rubyには24文字未満の文字列に対する最適化(文字列の埋め込み)が組み込まれています。だからこそ、24文字を境にメモリ使用量が跳ね上がるのです。詳細についてはPat Shaughnessyの記事で丁寧に解説されています。
エイリアスメソッドを見つける方法
Rubyの全エイリアスメソッドをまとめた「マスターリスト」があったら便利だと思いませんか?
その願い、叶えます!
こちらをご覧ください:
class Module
def aliased_methods
instance_methods(false)
.group_by { |m| instance_method(m) }
.map(&:last)
.keep_if { |symbols| symbols.length > 1 }
end
end
このコードはStack Overflowのある回答から引用したものです。Moduleクラスにaliased_methodsメソッドを定義し、instance_methodsメソッドでクラスに定義されたすべてのインスタンスメソッドの一覧を取得しています。
少し混乱するかもしれませんが、それがメタプログラミングというものです!
続いてのコードでは、少なくとも1つの「有効な」オブジェクトを持つクラス名の配列を作成し、各クラスに対してaliased_methodsを呼び出して結果を出力します。
objects = ObjectSpace.each_object.map(&:class).uniq
objects.each do |klass|
methods = "\n#{klass}\n#{'-'*20}\n"
klass.send(:aliased_methods).each do |m1, m2|
methods << "#{m1.to_s.ljust(15)} #{m2}\n"
end
puts methods
end
出力はこのようになります:
Array -------------------- inspect to_s [] slice length size find_index index collect map collect! map!
まとめ
ObjectSpaceでできるクールなテクニックを学べて、楽しんでもらえたなら嬉しいです。ぜひ実際に試してみて、何か面白い発見があれば教えてください!
この記事をプログラマーの友人たちにもぜひシェアしてください。彼らの新しい学びになると同時に、私にとっても多くの読者につながります 🙂
-
Ruby on Railsとは?初心者にもわかる仕組み・魅力・学び方を徹底解説
Ruby on Railsとは? Ruby on Rails(略称:RoR)は、世界で最も人気のあるオープンソースのWebアプリケーションフレームワークです。プログラミング言語「Ruby」をベースに構築されており、シンプルなサイトから大規模で複雑なサービスまで、幅広いWebアプリケーションの開発を支援します。 そもそもフレームワークとは? フレームワークとは、ソフトウェア開発の際に土台となる構造を提供してくれるコードやツール、ユーティリティの集合体です。あらかじめ用意された構造に沿ってコードを書くことで、プログラムが整理され、保守性も高まります。正しく使いこなせるようになれば、開発作業は格段に
-
Windowsログオンアプリケーション(winlogon.exe)とは?主な役割と安全性の確認方法
Windowsでは、パソコンの操作中に常に多数のプロセスがバックグラウンドで動作しています。Runtime BrokerやSystem Interrupts、DWM(デスクトップ ウィンドウ マネージャー)といったプロセス名を目にしたことがある方も多いでしょう。これらのプロセスはそれぞれ重要な役割を担い、Windowsの正常な動作を支えています。 その中でも特に知っておくべき重要なプロセスの一つが「Windowsログオンアプリケーション」、すなわちwinlogon.exeです。 今回は、Windowsログオンアプリケーションとは何か、なぜ重要なのかについて詳しく解説していきます。 Window