Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Ruby内部:Rubyオブジェクトのメモリレイアウトの調査

Ruby内部のクイックツアーをご希望ですか?

その後、あなたは御馳走になります。

なぜなら

Rubyオブジェクトがメモリ内にどのように配置されるか、そして内部データ構造を操作していくつかのクールなことを行う方法を一緒に探求します。

シートベルトを締めて、Rubyインタープリターの奥深くへの旅の準備をしてください!

アレイのメモリレイアウト

配列を作成するとき、Rubyはそれをシステムメモリと少しのメタデータでバックアップする必要があります。

メタデータに含まれるもの

  • 配列サイズ(アイテム数)
  • アレイ容量
  • クラス
  • オブジェクトのステータス(凍結されているかどうか)
  • データがメモリに保存される場所へのポインタ

メインのRubyインタープリター(MRI)はCで記述されているため、オブジェクトはありません。

しかし、他にも何かがあります:構造体

Cの構造体は、関連データを一緒に保存するのに役立ちます。これは、MRIのソースコードでArrayなどを表すためによく使用されます。 、String と他の種類のオブジェクト。

これらの構造体の1つを調べることで、オブジェクトのメモリレイアウトを推測できます。

それでは、Arrayの構造体を見てみましょう。 、RArrayと呼ばれます :

struct RArray {
  struct RBasic basic;

  union {
    struct {
      long len;

      union {
        long capa;
        VALUE shared;
      } aux;

      const VALUE *ptr;
    } heap;

    const VALUE ary[RARRAY_EMBED_LEN_MAX];
  } as;
};

Cに慣れていない場合、これは少し威圧的に見えるかもしれませんが、心配しないでください。私はあなたがこれを消化しやすいビットに分解するのを手伝います🙂

私たちが最初に持っているのは、このRBasicです。 構造体でもあるもの:

struct RBasic {
  VALUE flags;
  VALUE klass;
}

これはほとんどのRubyオブジェクトにあるものであり、このオブジェクトのクラスや、このオブジェクトがフリーズされているかどうかを示すいくつかのバイナリフラグ(および「汚染された」属性など)などが含まれています。

言い換えれば

RBasic オブジェクトの一般的なメタデータが含まれています。

その後、配列の長さ(len)を含む別の構造体があります。 。

ユニオン式は、aux capaのいずれかになります (容量の場合)またはshared 。これは主に最適化の問題であり、PatShaughnessyによるこの優れた投稿で詳しく説明されています。メモリ割り当てに関しては、コンパイラはユニオン内で最大の型を使用します。

次に、ptrがあります 、実際のArrayが存在するメモリアドレスが含まれています データが保存されます。

これがどのように見えるかの写真です(すべての白/灰色のボックスは32ビットシステムでは4バイトです ):

Ruby内部:Rubyオブジェクトのメモリレイアウトの調査

ObjectSpaceモジュールを使用して、オブジェクトのメモリサイズを確認できます。

require 'objspace'

ObjectSpace.memsize_of([])
# 20

これで、楽しい時間を過ごす準備ができました!

フィドル:楽しい実験

RBasicは、32ビットシステムでは正確に8バイト、64ビットシステムでは16バイトです。これを知っていると、Fiddleモジュールを使用して、オブジェクトの生のメモリバイトにアクセスし、楽しい実験のためにそれらを変更できます。

1ビットを切り替えることでフリーズ状態を変更できます。

これは本質的にフリーズメソッドが行うことですが、フリーズ解除メソッドがないことに注意してください。

楽しみのために実装しましょう!

まず、Fiddleを要求しましょう モジュール(Ruby標準ライブラリの一部)とフリーズされた文字列を作成します。

require 'fiddle'

str = 'water'.freeze
str.frozen?
# true

次へ:

文字列のメモリアドレスが必要です。これは次のように取得できます。

memory_address = str.object_id * 2

最後に:

Rubyがチェックする正確なビットを反転して、オブジェクトがフリーズしているかどうかを確認します。また、frozen?を呼び出して、これが機能するかどうかを確認します。 メソッド。

Fiddle::Pointer.new(memory_address)[1] ^= 8

str.frozen?
# false

インデックス[1]に注意してください フラグの2番目のバイトを指します 値(合計4バイトで構成されます)。

次に、^=を使用します これは、そのビットを反転するための「XOR」(排他的論理和)演算子です。

これを行うのは、フラグ内のさまざまなビットが 意味が異なり、無関係なものを変更したくありません。

私のルビートリックの投稿を読んだことがあるなら、これは以前に見たことがあるかもしれませんが、今ではそれがどのように機能するかを知っています🙂

配列の長さを変更して配列を印刷することもできます。

配列がどのように短くなるかがわかります!

クラスを変更してArrayを作成することもできます Stringだと思います …

結論

Rubyが内部でどのように機能するかについて少し学びました。 RubyオブジェクトのメモリのレイアウトとFiddleの使用方法 それをいじくり回すモジュール。

おそらくFiddleは使用しないでください 実際のアプリではこのようになりますが、試してみるのは楽しいです。

この投稿を共有することを忘れないでください より多くの人がそれを見ることができます🙂


  1. Rubyのデコレータデザインパターン

    デコレータのデザインパターンは何ですか? そして、Rubyプロジェクトでこのパターンをどのように使用できますか? デコレータデザインパターンは、新機能を追加することでオブジェクトを強化するのに役立ちます クラスを変更せずにそれに。 例を見てみましょう! ロギングとパフォーマンス この例では、rest-clientのようなgemを使用してHTTPリクエストを作成しています。 次のようになります: require restclient data = RestClient.get(www.rubyguides.com) 今 : 一部のリクエストにログを追加したいが、RestCli

  2. TCmallocを使用したRubyのメモリ割り当てのプロファイリング

    Rubyではメモリ割り当てはどのように機能しますか? Rubyはページと呼ばれるチャンクでメモリを取得し、新しいオブジェクトはここに保存されます。 次に… これらのページがいっぱいになると、より多くのメモリが必要になります。 Rubyは、mallocを使用してオペレーティングシステムからより多くのメモリを要求します 機能。 このmalloc 関数はオペレーティングシステム自体の一部ですが、使用できる代替の実装があります。 それらの実装の1つは、Googleのtcmallocです。 TCmallocはGoogleパフォーマンスツールスイートの一部です。 これらのツールを使用し