Hubble Advent Calendar 2023 9日目の記事です。
Hubble を運用する中で、Ruby のメモリ使用量を調べる方法とその制限について調べたので記録しておきます。
背景
Hubble にはバージョンという概念があり、契約書などの文書の版を管理できます。バージョンは親子関係で木構造になっていて、git log --graph のような外観を持っています。
このバージョン関係を走査してメンテナンスする際に、木構造の深さや高さによってどれくらいのメモリが必要なのかおおまかな把握が必要になりました。
メモリ使用量が正確ではない?
まず、Ruby オブジェクトのメモリ使用量は ObjectSpace.memsize_of で取得できます。単位はバイトです。
require "objspace"
ObjectSpace.memsize_of("hogehoge")
=> 40
ただし、ドキュメントに気になる一文がありました。
戻り値の内容は完全ではない事に注意してください。この内容はあくまでもヒントとして扱う必要があります。
memsize_of の出力は正確な値ではないようです。この値がどんなときにどれくらいずれるのかを調べてみました。
Ruby 内部のデータ構造
immediate value
Ruby の内部データ構造では immediate value という種類があり、たとえば一定範囲の整数や boolean、nil です。Ruby FAQ ではこのように解説されていました。
Fixnum, true, nil, and false are implemented as immediate values. With immediate values, variables hold the objects themselves, rather than references to them.
Ruby では全てのデータがオブジェクトで、C の実装レベルでは RObject という構造体がインスタンスの状態 = インスタンス変数を格納した領域へのポインタを ivptr として持っています。ただし immediate value は例外で、ポインタを格納する領域に実際の値を直接保存しているため、インスタンス状態の領域を持っていません。immediate value は複雑な構造を持たない単なる値なので、最適化のためにこういった実装になっているようです。
memsize_of ではインスタンス状態の領域の長さを集計しているので immediate value はカウントされず、その分がずれるようです。
> ObjectSpace.memsize_of(100)
=> 0
> ObjectSpace.memsize_of(true)
=> 0
Array のメモリ管理
他にも、Ruby のメモリ管理方針が原因でずれが生じる場合があるようです。
Ruby の Array は自由に要素を追加したり削除できる可変長配列ですが、配列の要素数が変わったときにメモリの割当をやり直します。より大きな連続したメモリ領域に改めてコピーし直したり、削除済みの要素を考慮して割り当てるメモリ領域を縮小します。
このメモリ領域の再割当やコンパクト化を、追加や削除のたびに毎回実行するのは負担が大きいので、その都度毎回ではなく何回かに一度まとめて実行します。下記のように、要素の数に比例してメモリ使用量が変わるのではなく、ある要素数を超えたときに一気に増えていることがわかります。
> array = ["1", "2", "3", "4"]
> ObjectSpace.memsize_of(array)
=> 72
> array << "5"
> ObjectSpace.memsize_of(array)
=> 208
> array << "6"
> ObjectSpace.memsize_of(array)
=> 208
> array << "7"
> ObjectSpace.memsize_of(array)
=> 208
> array << "8"
> ObjectSpace.memsize_of(array)
=> 208
そのほか
ガベージコレクションの状態など、ほかにも様々な条件でずれが生じるようです。
まとめ
- 特定の種類の値 = immediate value の分がずれる
- 動的に Array の操作をするとずれる場合がある
今回の用途は概算なので、問題にならないことがわかりました。
また、ここには書いていないのですが、特定のクラスは要素数が 3 つ以下のときにメモリ使用量が最小になる、などの知識を得られました。別の機会に、業務での活用方法を含めて掘り下げてみたいと思います。
明日はフロントエンドの @beltway7 さんです!