12
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

HubbleAdvent Calendar 2023

Day 13

Ruby オブジェクトのメモリ使用量

Last updated at Posted at 2023-12-12

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 さんです!

12
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?