同じ文字列でもオブジェクトIDは違う
Rubyには、オブジェクトを識別するためのオブジェクトIDというものがあります。同じ文字列を変数に格納してもオブジェクトIDは変わってきます。
#string 10.times do str = 'hoge' p str.object_id end #=>70145884221500 #=>70145888772040 #=>70145888771960 #=>70145888771880 #=>70145888771800 #=>70145888771720 #=>70145888771640 #=>70145888771560 #=>70145888771480 #=>70145888771400 #symbol 10.times do str = :hoge p str.object_id end #=>832988 #=>832988 #=>832988 #=>832988 #=>832988 #=>832988 #=>832988 #=>832988 #=>832988 #=>832988 #Integer 10.times do num = 1 p num.object_id end #=>3 #=>3 #=>3 #=>3 #=>3 #=>3 #=>3 #=>3 #=>3 #=>3
※繰り返しの理由が10回なのはとくに理由はありません
stringについてみてみると、「 hoge」という文字列を変数strに格納し、オブジェクトIDを出力するということを10回行っています。結果は10回とも違うオブジェクトIDが出力されました。これは10回オブジェクトが生成されているということです。
symbolとInteger(細かく言うとFixnum。Bignumも同様の動作をする)はオブジェクトIDが全部同じ。これは仕様でオブジェクトを複数回生成しないようになってるからみたいです。
Rubyのオブジェクトのメモリについて
この前こんな記事書きました。yoskmr.hatenablog.com
オブジェクトのメモリの使用量を調べるモジュールありますよという記事です。この時に謎のままにしていた以下のコードの2行目の「GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]」すが、調べたら正体がわかったので書きます。
結論から言うと、「ヒープ領域に格納したRubyオブジェクトのポインタのメモリサイズ」です。では説明していきます。
require 'objspace' rvalue_size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
Rubyのオブジェクトはヒープ領域で値型変数にポインタとして格納される
※値型=VALUE Type、これともう一つ、参照型=Reference Typeてのがあります。
詳しくはこちら→ 値型と参照型 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
Rubyの話だったけど、ほとんどCの話になります笑
図1:Rubyでのメモリ領域
引用:http://www.is.titech.ac.jp/~sassa/lab/papers-written/08M37090-oota.pdf
Rubyのオブジェクト領域(図1でいえばヒープの下の領域)のオブジェクトは全てヒープ領域に値型変数にポインタとして格納されます。ヒープ領域の値型はRVALUEデータ構造という規定になっている(ガーベージコレクションやデータ型の情報も格納されている)らしい。
Rubyのオブジェクトのメモリサイズ
上記のことをふまえ、下のコードを確認してみましょう。
require 'objspace' rvalue_size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE] num = 1 p ObjectSpace.memsize_of(num) # => 0 p ObjectSpace.memsize_of(num) + rvalue_size # => 40 str = 'a' p ObjectSpace.memsize_of(str) # => 40 p ObjectSpace.memsize_of(str) + rvalue_size # => 80
やっと本題ですね。
「objspace」というモジュールを使用して、オブジェクトのメモリサイズを調べられるのですが、numもstrも、それぞれmemsize_ofメソッドだけの時と、rvalue_seize(GC::INTERNAL_CONSTANTS[:RVALUE_SIZE])をプラスした時でメモリサイズが違う。これはmemsize_ofメソッドがRuby領域のオブジェクトのメモリサイズしか出力しないからみたいです。では「rvalue_size」は何者かと言うと、先ほど説明したヒープ領域で格納したオブジェクトのポインタです。
つまりこういうことです。
- 【実際のメモリ】=【Rubyのオブジェクト領域でのメモリ】+【ヒープ領域でのポインタ格納のメモリ】
そこで、もう一つの疑問、numオブジェクトのRubyオブジェクト領域でのメモリサイズがゼロなんだけどどういうこと?です。これは、ガーベージコレクションという動的に確保したメモリのうち、不要になった領域を開放するという機能を最適化するための例外です。どういった例外かというと、「Fixnumオブジェクト,Symbolオブジェクト,true,false,nilオブジェクト」は関しては他のオブジェクトと違って扱い方を例外にしますということである。他のオブジェクトはヒープ領域にRVALUEデータ構造(ポインタ)として値型に格納されますが、例外のオブジェクトは値型には直接格納しちゃいましょうという仕様である。Fixnumオブジェクトの整数の「1」もオブジェクトであるが「1」のためにRubyオブジェクト領域にメモリを確保するのはガーベージコレクションの処理では非効率になることらしい。なので、上記コードの整数であるnumはmemsize_ofメソッドではメモリがゼロになったのである。
参考
以下のサイト、論文を参考にしました。感謝です。
http://www.is.titech.ac.jp/~sassa/lab/papers-written/08M37090-oota.pdf (Rubyにおけるメモリ管理改善手法の提案)
http://www.atdot.net/~ko1/activities/rubyfp2008.pdf (Ruby処理系での軽量な浮動小数点数表現 )
ガベージコレクション
第5章 ガ−ベージコレクション
RubyのFixnum
こんちはっす
Rubyの世界は全てオブジェクトである。ということを頭に叩き込んで勉強しております。しかし、最近奇妙なものにぶつかりました。
変数はオブジェクトを指すタグであって、変数は直接オブジェクトを保持しているわけではないという認識です。
下のコードで、hogeメソッドにはIntegerのオブジェクトを仮引数としてわたし、fugaメソッドにはArrayのオブジェクトを仮引数としてわたしました。hogeメソッドを実行すると実引数は変化なし、fugaメソッドを実行すると実引数が変更されました。hogeメソッド実行するとhogeメソッドにいれた実引数も変更されると思ったのですが、変更されませんでした。オブジェクトなのに?
def hoge(a, b) a += 10 b += 10 end x = 1 y = 2 p x.object_id #=> 3 p y.object_id #=> 5 hoge(x, y) p x.object_id #=> 3 p y.object_id #=> 5 #hogeメソッド実行結果 p x #=> 1 p y #=> 2 def fuga(ary) ary[0] += 10 ary[1] += 10 end array = [1, 2] p array.object_id #=> 70226961071960 p array[0].object_id #=> #=> 3 p array[1].object_id #=> #=> 5 fuga(array) p array.object_id #=> 70226961071960 p array[0].object_id #=> 23 p array[1].object_id #=> 25 #fugaメソッド実行結果 p array #=> [11, 12]
※object_idについて
object_idメソッドは、レシーバのオブジェクトIDを返します。オブジェクトIDは、オブジェクトごとに固有の整数値です。レシーバが同じオブジェクトなら必ず同じオブジェクトIDになり、違うオブジェクトなら別のオブジェクトIDが返ります。
object_id (Object) - Rubyリファレンス
アドレスではないみたいです。
Fixnumは特別扱い
では、Integerのオブジェクトが何で変更されなかったかについて
rubyではオブジェクトの実体を構造体で表現し、扱うときは常に ポインタ経由で扱う。第2章 オブジェクト
とのことですが、「小さな整数」は特別にポインタとして扱わず直接値として扱うという仕様があるらしいのです。てか、小さな整数ってなんですかていう話ですが、そいつの正体は「Fixnum」のことです。Fixnumの親クラスはIntegerです。Fixnumと同レベルにBignumというものがあります。Fixnumだった整数がある値を超えるとFixnumからBignumに変わります。仕様で整数の大きさでクラスが変わるみたいです。それでいつクラスが変わるのか?なんですが、調べている方がいました。感謝です。
4611686018427387903.class #=> Fixnum 4611686018427387904.class #=> Bignum -4611686018427387904.class #=> Fixnum -4611686018427387905.class #=> Bignum
つまり小さな整数を扱う場合は、直接整数を扱っているということになっているのである。なので、小さな整数Fixnumだと値を参照しているのではなく、値を直接保持しているということになっている。知らなかった。
これは、整数インスタンスが多すぎて、そんなに多くインスタンスつくるのはメモリ管理の手間なので、値が小さいときは特別扱いにするという仕様なんだと。
ちなみに、SymbolもFixnumと同じく、特別扱いになるらしい。
Rubyの変数と参照値渡し
AOJでRubyで競技プロの問題解いてて、Rubyってメモリ消費が多いんだなーてことを実感。Rubyのオブジェクトがどれくらいメモリを消費しているのか確認できる方法調べてみました。
require 'objspace' rvalue_size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE] num = 1 p ObjectSpace.memsize_of(num) # => 0 p ObjectSpace.memsize_of(num) + rvalue_size # => 40 str = 'a' p ObjectSpace.memsize_of(str) # => 40 p ObjectSpace.memsize_of(str) + rvalue_size # => 80 array = [] p ObjectSpace.memsize_of(array) # => 40 p ObjectSpace.memsize_of(array) + rvalue_size # => 80 hash = {foo: '1'} p ObjectSpace.memsize_of(hash) # => 232 p ObjectSpace.memsize_of(hash) + rvalue_size # => 272
しかし、確認しようと
ObjectSpace.memsize_of()
をたたいてIntegerを確認してみると 0 という数字が返ってびっくりです。これはCレベルで最適化され、オブジェクトが生成されないほげほげ...とのことです(次回調べてみます)。
オブジェクトが生成されないということで、以下のようなことが起こるみたいです。(今回の本題)
Rubyの変数は値渡しらしく、これは変数を他の変数に代入するときには、元変数の値が代入先の変数の値になるということ。しかし、Rubyの世界は全てオブジェクトなので、変数の値は「オブジェクトのアドレス」になるのである。これが参照値渡し。
つまり、オブジェクトはアドレスをもっており、そのアドレスの先に値を保持しているのである。
それを前提として以下のコードを確認してみる
def hoge(a, b) a += 10 b += 10 end x = 1 y = 2 p x.object_id #=> 3 (xには3というObjectIDが振られている) p y.object_id #=> 5 (yには5というObjectIDが振られている) hoge(x, y) p x.object_id #=> 3 (xには3というObjectIDが振られている) p y.object_id #=> 5 (yには5というObjectIDが振られている) #hogeメソッド実行結果 p x #=> 1 p y #=> 2 def fuga(ary) ary[0] += 10 ary[1] += 10 end array = [1, 2] p array.object_id #=> 70226961071960 (arrayには70226961071960というObjectIDが振られている) p array[0].object_id #=> #=> 3 (array[0]には3というObjectIDが振られている) p array[1].object_id #=> #=> 5 (array[1]には5というObjectIDが振られている) fuga(array) p array.object_id #=> 70226961071960 (arrayには70226961071960というObjectIDが振られている) p array[0].object_id #=> 23 (array[0]には23というObjectIDが振られている) p array[1].object_id #=> 25 (array[1]には25というObjectIDが振られている) #fugaメソッド実行結果 p array #=> [11, 12]
仮引数を配列オブジェクトにすると、元の数字も変更された。
これは、オブジェクトが直接値を保持しているのではなく、数字を保持しているアドレスを保持しているからである。fugaメソッドは配列array[]が保持しているアドレスを受け取ったことになります。
それじゃ、整数はIntegerのオブジェクトじゃないの?という疑問が生まれます。
それについてはこちらRubyのFixnum - nil
Rubyのクラスについて
Rubyはすべてオブジェクトなのでクラスもオブジェクトです。クラスはClassクラスのインスタンスになります。
classを定義方法は以下、
class hoge end
Classクラス
Classクラスにも親がいます。ancesotresメソッドを使って確認してみます。
Class.ancestors #=> [Class, Module, Object, Kernel, BasicObject]
Classクラスで使用できるメソッドの多くはModuleクラスで定義されています。両者の違いは
- Classはインスタンスが生成できる
- Classは継承する・されることができる
Classクラスで定義されているメソッドは上記2つの機能しかもっていません。では、確認してみます。
Class.instance_methods(false) #=> [:allocate, :new, :superclass]
allocateメソッド:そのクラスのインスタンスとなるオブジェクトを生成しますが、initializeメソッドの呼び出しは行われません。戻り値はnil。
newメソッド:そのクラスのインスタンスとなるオブジェクトを生成し、その際にinitializeメソッドが呼び出します。戻り値は新しいオブジェクト。
superclassメソッド:そのクラスの親クラスを返します。戻り値はクラスオブジェクト。
Vagrant にホスト名使って ssh でログインしてみた
勉強しながらブログ書こうと思ってたらブログつくる忘れてた
昔、学校でプログラミングの講義が会った時はコピペの乗り切った糞野郎です。
講義中はゲームしてました(先生、すみません)。
何でまた急にプログラマーを目指そうかと思ったのは、自分の手で形になるものをつくれるようになりたい、かつ、つくったもが人に触れる機会がネット上だと多いからです。あと、もくもくと作業をするのが好きだということに気がついたから。
てことで、初めにUNIXのコマンドについて勉強しようと思ったので、ドットインストールで勉強しようと思ったので見てみたら、
UNIXコマンド入門 (一般ユーザー編) (全16回) - プログラミングならドットインストール
ローカル開発環境どうのこうのでてきて、「?」状態です。
なので、さかのぼって
ローカル開発環境の構築 [MacOS X編] (全9回) - プログラミングならドットインストール
これを勉強しました。
かなり手こずりましたが、なんとかローカル開発環境を立ち上げることが出来ました。昔、学校でコマンドライン使った事あったので何とかなりましたが、全くの初心者からだとハードルは高いのかなーと思いました。
てことで、次はUNIXコマンド入門勉強していきます。