Rubyでメモリ膨張を減らす方法
Rubyアプリケーションのメモリ膨張の問題は、頻繁に議論されるトピックです。この投稿では、Rubyのメモリ管理がうまくいかない可能性があることと、Rubyアプリケーションが爆発するのを防ぐために何ができるかを見ていきます。
まず、アプリケーションのメモリのコンテキストで肥大化が何を意味するかを理解する必要があります。
飛び込みましょう!
Rubyのメモリ膨張とは何ですか?
メモリの肥大化とは、アプリケーションのメモリ使用量が明確な説明なしに突然増加することです。この増加は迅速な場合とそうでない場合がありますが、ほとんどの場合、継続的になります。これは、一定期間に複数の実行が行われた結果として発生するメモリリークとは異なります。
メモリの肥大化は、本番環境でRubyアプリケーションに発生する最悪の事態の1つになる可能性があります。ただし、適切な対策を講じれば回避できます。たとえば、アプリケーションのメモリ使用量の急増に気付いた場合は、他の問題のトラブルシューティングを行う前に、メモリの肥大化の兆候を確認することをお勧めします。
メモリの肥大化を診断して修正する方法を探る前に、Rubyのメモリアーキテクチャについて簡単に説明しましょう。
ルビーの記憶構造
Rubyのメモリ使用量は、利用可能なシステムリソースの賢明な使用を管理する特定の要素を中心に展開されます。これらの要素には、Ruby言語、ホストオペレーティングシステム、およびシステムカーネルが含まれます。
これらとは別に、ガベージコレクションプロセスは、Rubyメモリの管理方法と再利用方法を決定する上でも重要な役割を果たします。
ルビーヒープページとメモリスロット
Ruby言語は、オブジェクトをヒープページと呼ばれるセグメントに編成します。ヒープスペース全体(使用可能なメモリ)は、使用済みセクションと空のセクションに分割されます。これらのヒープページはさらに同じサイズのスロットに分割され、それぞれ1つのユニットサイズのオブジェクトが許可されます。
新しいオブジェクトにメモリを割り当てるとき、Rubyは最初に使用済みヒープスペースで空きスロットを探します。何も見つからない場合は、空のセクションから新しいヒープページを割り当てます。
メモリスロットは小さなメモリ位置であり、それぞれのサイズは約40バイトです。これらのスロットからオーバーフローしたデータは、ヒープページの外側の異なる領域に格納され、各スロットには外部情報へのポインタが格納されます。
システムのメモリアロケータは、ヒープページや外部データポインタなど、Rubyランタイム環境ですべての割り当てを行います。
Rubyでのオペレーティングシステムのメモリ割り当て
Ruby言語によって行われたメモリ割り当て呼び出しは、ホストオペレーティングシステムのメモリアロケータによって処理および応答されます。
通常、メモリアロケータは、C関数のグループ、つまり malloc で構成されます。 、 calloc 、 realloc 、および無料 。それぞれを簡単に見てみましょう:
- マロック :Mallocはメモリ割り当ての略で、オブジェクトに空きメモリを割り当てるために使用されます。割り当てられるメモリのサイズを取り込み、割り当てられたメモリブロックの開始インデックスへのポインタを返します。
- Calloc :Callocは連続割り当ての略で、Ruby言語がメモリの連続ブロックを割り当てることを可能にします。既知の長さのオブジェクト配列を割り当てる場合に役立ちます。
- Realloc :Reallocは再割り当ての略で、言語が新しいサイズでメモリを再割り当てできるようにします。
- 無料 :Freeは、事前に割り当てられたメモリ位置のセットをクリアするために使用されます。解放する必要のあるメモリブロックの開始インデックスへのポインタを取り込みます。
Rubyのガベージコレクション
言語ランタイムのガベージコレクションプロセスは、使用可能なメモリの使用率に劇的な影響を及ぼします。
Rubyには、上記のAPIメソッドをすべて使用してアプリケーションのメモリ消費を常に最適化する非常に高度なガベージコレクションがあります。
Rubyのガベージコレクションプロセスに関する興味深い事実は、アプリケーション全体が停止することです。これにより、ガベージコレクション中に新しいオブジェクトの割り当てが発生しなくなります。このため、ガベージコレクションルーチンは頻繁ではなく、可能な限り迅速に行う必要があります。
Rubyでのメモリ膨張の2つの一般的な原因
このセクションでは、Rubyでメモリの膨張が発生する最も重要な2つの理由、断片化とリリースの遅延について説明します。
メモリの断片化
メモリの断片化とは、メモリ内のオブジェクトの割り当てが全体に分散し、空きメモリの連続するチャンクの数を減らすことです。ディスクに十分な空きメモリがある場合でも、連続したブロックがないとメモリをオブジェクトに割り当てることはできません。この問題は、どのプログラミング言語または環境でも発生する可能性があり、各言語には問題を解決するための方法があります。
断片化は、言語のレベルとメモリアロケータのレベルの2つの異なるレベルで発生する可能性があります。これらの両方を詳しく見てみましょう。
Rubyレベルでの断片化
言語レベルでの断片化は、ガベージコレクションプロセスの設計により発生します。ガベージコレクションプロセスは、Rubyヒープページスロットを空きとしてマークし、そのスロットを再利用してメモリ内の別のオブジェクトを割り当てることができるようにします。完全なRubyヒープページが空きスロットのみで構成されている場合、そのヒープページをメモリアロケータに解放して再利用できます。
しかし、ヒープ上で少数のスロットが空きとしてマークされていない場合はどうなりますか?メモリアロケータに解放されることはありません。ここで、ガベージコレクションによって同時に割り当てられ解放されるさまざまなヒープページの多くのスロットについて考えてみましょう。ヒープページ全体が一度にリリースされる可能性はほとんどありません。ガベージコレクションプロセスはメモリを解放しますが、メモリブロックが部分的に占有しているため、メモリアロケータで再利用することはできません。
メモリアロケータレベルでの断片化
メモリアロケータ自体も同様の問題に直面しています。OSヒープがすべて完全に解放されたら、それらを解放する必要があります。ただし、ガベージコレクションプロセスのランダムな性質を考慮すると、OSヒープ全体を一度に解放できる可能性はほとんどありません。
メモリアロケータは、アプリケーションで使用するためにシステムメモリからOSヒープもプロビジョニングします。既存のヒープにアプリケーションのメモリ要件を満たすのに十分な空きメモリがある場合でも、新しいOSヒープをプロビジョニングするために移動するだけです。これは、アプリケーションのメモリメトリックが急上昇するための完璧なレシピです。
スローリリース
Rubyでのメモリ膨張のもう1つの重要な原因は、解放されたメモリがシステムにゆっくりと解放されることです。この状況では、メモリは、新しいメモリブロックがオブジェクトに割り当てられる速度よりもはるかにゆっくりと解放されます。これは解決すべき従来の問題や新人の問題ではありませんが、記憶の肥大化に大きな影響を及ぼします。断片化よりもさらに深刻です。
メモリアロケータのソースを調査したところ、アロケータはOSヒープの最後で、それでもごくまれにOSページを解放するように設計されていることがわかりました。これはおそらくパフォーマンス上の理由によるものですが、逆効果になり、逆効果になる可能性があります。
Rubyメモリの肥大化を修正する方法
Rubyのメモリが肥大化する原因がわかったので、これらの問題を修正し、デフラグとトリミングによってアプリのパフォーマンスを向上させる方法を見てみましょう。
デフラグによるRubyメモリの肥大化の修正
ガベージコレクションの設計が原因で断片化が発生し、それを修正するためにできることはあまりありません。ただし、メモリディスクが断片化する可能性を減らすために実行できる手順がいくつかあります。
- かなりの量のメモリを使用するオブジェクトへの参照を宣言する場合は、そのジョブが完了したときに手動で解放するようにしてください。
- すべての静的オブジェクト割り当てを1つの大きなブロックで宣言してみてください。これにより、すべての永続クラス、オブジェクト、およびその他のデータが同じヒープページに配置されます。後で動的割り当てを試すときに、静的ヒープページについて心配する必要はありません。
- 可能であれば、最初ので大規模な動的割り当てを行うようにしてください。 あなたのコードの。これにより、それらがより大きな静的割り当てメモリブロックに近くなり、残りのメモリがクリーンに保たれます。
- 小さくてめったにクリアされないキャッシュを使用する場合は、最初に永続的な静的割り当てを使用してグループ化することをお勧めします。アプリのメモリ管理を改善するために、完全に削除することを検討することもできます。
- 標準のglibcメモリアロケータの代わりにjemallocを使用します。この小さな調整により、Rubyのメモリ消費量を最大4分の1に減らすことができます。ここでの唯一の注意点は、すべての環境で互換性があるとは限らないことです。そのため、本番環境に移行する前に、必ずアプリを徹底的にテストしてください。
Rubyメモリの肥大化を修正するためのトリミング
遅いメモリ解放を修正するには、ガベージコレクションプロセスをオーバーライドし、メモリをより頻繁に解放する必要があります。 malloc_trimと呼ばれるこれを実行できるAPIがあります 。ガベージコレクションプロセス中にこの関数を呼び出すようにRubyを変更するだけです。
malloc_trimを呼び出す変更されたRuby2.6コードは次のとおりです。 gc.c関数のgc_start
:
gc_prof_timer_start(objspace);
{
gc_marks(objspace, do_full_mark);
// BEGIN MODIFICATION
if (do_full_mark)
{
malloc_trim(0);
}
// END MODIFICATION
}
gc_prof_timer_stop(objspace);
注: これは、アプリを不安定にする可能性があるため、本番アプリケーションではお勧めしません。ただし、メモリの解放が遅いとパフォーマンスに大きな打撃を与え、解決策を試す準備ができている場合に便利です。
まとめと次のステップ
記憶の膨張は、特定するのが難しく、修正するのがさらに困難です。
この記事では、Rubyアプリのメモリ膨張の背後にある2つの重要な理由(断片化とリリースの遅延)と、2つの修正(最適化とトリミング)について説明しました。
差し迫った肥大化のインシデントを特定し、アプリがダウンする前に修正するには、アプリの指標を常に監視する必要があります。
Rubyアプリケーションのメモリの肥大化を修正するためのいくつかの手順を実行するのに役立つことを願っています。
P.S。 Ruby Magicの投稿をマスコミから離れたらすぐに読みたい場合は、Ruby Magicニュースレターを購読して、投稿を1つも見逃さないでください。
ゲスト著者のKumarHarshは、クラフトによる新進気鋭のソフトウェア開発者です。彼は、RubyやJavaScriptなどの人気のあるWebテクノロジーに関するコンテンツをまとめる元気なライターです。彼の詳細については、彼のWebサイトを参照し、Twitterでフォローしてください。
-
RubyでStructとOpenStructを使用する方法
Rubyの構造体とは何ですか? 構造体は組み込みのRubyクラスであり、値オブジェクトを生成する新しいクラスを作成するために使用されます。値オブジェクトは、関連する属性を一緒に格納するために使用されます。 ここに例があります : Point 2つの座標(x &y 。 このデータはさまざまな方法で表すことができます。 いいね : 配列[10, 20] ハッシュ{ x: 10, y: 10 } オブジェクトPoint.new(10, 20) 複数のPointを使用する場合 、オブジェクトアプローチを使用することをお勧めします。 しかし… これら2つの値を一緒に格納するた
-
Google chrome Windows 10 での高いメモリ使用量を減らす方法
Google chrome は、インターネット愛好家の間で最も使用されている Web ブラウザーの 1 つであり、ブラウジング体験をより速く、よりスムーズに、より簡単かつ安全にする、最小限で真にクリーンな環境で人気があります。しかし、ユーザーが Google chrome について報告する最大の不満の 1 つは、ブラウザーが大量のシステム RAM を使用することです。最終的には、Chrome ブラウザーと Windows 10 コンピューターの速度が低下し、全体的なユーザー エクスペリエンスに影響を与えます。この投稿では、Chrome のメモリ使用量を減らすためのヒントをいくつか紹介します。