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

ガベージコレクションの概要(パートII)

Ruby Magicの前回のエピソードでは、ガベージコレクション(GC)が必要な理由と、それが一般的にどのように機能するかについて説明しました。この投稿では、これがRubyでどのように実装されているかについてもう少し詳しく説明します。

さまざまなRubyの実装

Rubyには多くの実装があります。人気のあるものは、MRI(MatzのRubyインタープリター)、Rubinius、JRubyの3つです。 Rubyの実装が異なれば、GCのメソッドも異なります。この記事では、ほとんどのRuby開発者が使用するMRIに焦点を当てます。

ルビーのヒープ

コンピュータには、スタックとヒープの2種類のメモリがあります。スタックは非常に高速で、関数呼び出しのコンテキストに対してローカルです。これは、関数が実行されると、スタックで宣言されているすべての変数がすぐに解放されることを意味します。スタックのサイズは非常に限られているため、たとえば、画像やファイルのデータを含む大きなオブジェクトを保存することはできません。

これは、Rubyオブジェクトを格納するのに適していません。これらのオブジェクトは、メソッド呼び出しよりも長く続くことがよくあります。また、オブジェクトがスタックに対して大きすぎるかどうかを予測することはほとんど不可能です。

したがって、Rubyは他のタイプのメモリであるヒープを使用します。ヒープ上で、プログラムはメモリを要求し、そのメモリで実行された後のクリーンアップを担当します。 Rubyは、Rubyオブジェクトの格納に使用する単一のメモリスラブを要求することでこれを使用します。これはRubyのヒープと呼ばれます。

スタックとヒープの概要
スタックメモリ 非常に高速
関数呼び出しが終了すると、使用済みメモリが自動的に解放されます
サイズが非常に制限されています
ヒープメモリ スタックよりも少し遅い
自動クリーンアップなし
サイズは、コンピュータの使用可能なメモリによってのみ制限されます

したがって、Rubyオブジェクトは常にRubyのヒープに割り当てられます。それらが40バイトよりも小さい場合、それらのコンテンツをオブジェクトに直接埋め込むことができます。それ以外の場合、オブジェクトはRubyのヒープ上のメモリの別のセグメントを指します。そのため、Rubyオブジェクトは、メモリ内の2つの完全に異なる場所に格納されることがよくあります。 Rubyのヒープがいっぱいになると、新しいヒープが作成され、新しいオブジェクトに使用されます。

マークアンドスイープ

MRIは、マークアンドスイープと呼ばれるGCアルゴリズムを使用します。これは、最初にマークフェーズを実行することによって機能します。マークフェーズでは、ガベージコレクターが現在存在するすべてのオブジェクトをスキャンし、クリーンアップできると思われるすべてのオブジェクトにマーク付きフラグを設定します。

マークフェーズは、コードの実行を停止します。この理由は、ガベージコレクターが存在するオブジェクト間のすべての関係を理解する必要があるためです。マークプロセス中にプログラムが実行されている場合、その間に状況が変化する可能性があり、ガベージコレクターはオブジェクトの現在の状態が何であるかを確認できません。

次に、スイープフェーズが開始されます。これは、Ruby1.9以降ではバックグラウンドで実行されます。ガベージコレクターは、マークフェーズでマークされたすべてのオブジェクトを静かに解放します。メモリはスイープ後にのみ再び使用可能になります。

マークフェーズはコードの実行を停止するため、ここで本番環境で問題が発生する可能性があります。スイープフェーズは比較的穏やかです。

マークアンドスイープの概要
マークフェーズ 既存のオブジェクトをスキャンします
オブジェクトをクリーンアップできる場合はマークフラグを設定します
コードの実行を停止します
スイープフェーズ バックグラウンドで実行
マークされたオブジェクトをクリーンアップします
スイープが実行された後、メモリが再び利用可能になります

メジャーおよびマイナーGCの実行

Ruby 2.1以降では、ガベージコレクターはメジャーランとマイナーランを実行します。どのオブジェクトが新しいかを追跡します。オブジェクトが数回のGC実行を生き残った場合、それは古いものとしてマークされます。その後、古いオブジェクトはマイナー実行で無視されます。これにより、ガベージコレクターは割り当てられたばかりのオブジェクトをスキャンするだけで済むため、マイナーランの邪魔になりません。

多くの場合、メモリの一部をクリーンアップする必要がないため、これは便利です。 Railsを起動すると、Railsフレームワーク全体がメモリにロードされます。それは生涯にわたってそこにとどまるので、これらすべてのオブジェクトを毎回チェックするのはリソースの無駄になります。

メジャーランとマイナーラン
メジャーラン 実行頻度が低い
実行がより集中的
マイナーラン 古いオブジェクトを無視します
より頻繁に実行されます
実行の邪魔になりません

もっと深く潜りたい人のために

これについては、AmanGuptaのすばらしいブログから多くのことを学びました。このテーマについてさらに深く掘り下げたい場合は、チェックしてください。

次へ:実用的なガベージコレクションの調整

ガベージコレクターの動作を調整するために測定できるメトリックと構成の変更がいくつかあります。このGCシリーズの次回の記事では、これらのメトリックと構成パラメーターについて説明します。


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

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

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

    Ruby内部のクイックツアーをご希望ですか? その後、あなたは御馳走になります。 なぜなら … Rubyオブジェクトがメモリ内にどのように配置されるか、そして内部データ構造を操作していくつかのクールなことを行う方法を一緒に探求します。 シートベルトを締めて、Rubyインタープリターの奥深くへの旅の準備をしてください! アレイのメモリレイアウト 配列を作成するとき、Rubyはそれをシステムメモリと少しのメタデータでバックアップする必要があります。 メタデータに含まれるもの : 配列サイズ(アイテム数) アレイ容量 クラス オブジェクトのステータス(凍結されているかどうか) データが