39

Ruby 2.0 では、コピー オン ライトに適したガベージ コレクターが導入されています。私のプロセスは、メモリを数分以上共有しているようには見えません.shared_dirtyからprivate_dirtyに急速に移行しているようです.

他の何人かはこれを機能させることに成功しました:

このプログラムを使用して、Linux のメモリ統計を確認できます: https://gist.github.com/kenn/5105061

私のユニコーン構成: https://gist.github.com/inspire22/f82c77c0a465f1945305

なんらかの理由で、同じく preload_app=true を使用している私のユニコーン アプリでは、共有メモリがはるかに少なくなっています。Ruby 2.0-p195、rails 3.2、linux 2.6.18 (centos)

[root@thorn script]# ruby memstats.rb 4946
Process:             4946
Command Line:        unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D
Memory Summary:
  private_clean                   0 kB
  private_dirty              56,324 kB
  pss                        60,256 kB
  rss                        83,628 kB
  shared_clean                4,204 kB
  shared_dirty               23,100 kB
  size                      108,156 kB
  swap                           68 kB 

マスター プロセスを (HUP だけでなく) 完全にシャットダウンしてから再起動し、要求がキューに入る前にすぐにワーカーをチェックすると、より良い話が得られます。

[root@thorn script]# ruby memstats.rb 5743
Process:             5743
Command Line:        unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D
Memory Summary:
  private_clean                   0 kB
  private_dirty              21,572 kB
  pss                        27,735 kB
  rss                        66,296 kB
  shared_clean                2,484 kB
  shared_dirty               42,240 kB
  size                       91,768 kB
  swap                            0 kB

しかし、起動してから 5 秒以内に、約 20MB の shared_clean+shared_dirty に戻ります。

スワッピングが問題を引き起こしているのではないかと疑っていましたが、swappiness を下げ、(swapstats.rb を使用して) 親プロセスも子プロセスもスワップアウトされていないことを確認した後も、問題は解決しません。

shared_dirty メモリとは何か、それがどのようにしてプライベート メモリに変わるのか正確にはわかりません。また、共有メモリの寿命と量を改善するための提案も大好きです。ありがとう!

4

1 に答える 1

7

この回答によると、既にご覧になっているかもしれませんが、次の行があります。

「共有可能な」ページは、実際に共有されるまでプライベート マッピングとしてカウントされることに注意してください。つまり、現在 libfoo を使用しているプロセスが 1 つしかない場合、そのライブラリのテキスト セクションがプロセスのプライベート マッピングに表示されます。別のプロセスがそのライブラリの使用を開始した場合にのみ、共有マッピングで考慮されます (プライベート マッピングからは削除されます)。

この記事で説明したメリットが得られるかどうかをテストするために、10 MB の xml ファイルをリテラル文字列としてソース コードに直接挿入します。次に、20 個のワーカーを起動すると、新しいガベージ コレクション機能で予想されるように、200MB のメモリを使用しているか、10MB しか使用していないかを確認できます。

アップデート:

私はユニコーンのソースを調べていて、この素晴らしい記事への参照を見つけました。

要約すると、Ruby Enterprise Edition のコピー オン ライトに適したガベージ コレクターを利用するようにアプリケーションを適応させるには、fork する前に GC.copy_on_write_friendly を true に設定する必要があると述べています。

if GC.respond_to?(:copy_on_write_friendly=)
    GC.copy_on_write_friendly = true
end

提供されたユニコーン構成ファイルに基づくと、割り当てが欠落しているようです。

また、以下の関連記事も楽しく読ませていただきました。

フォークのマニュアルページによると:

Linux では、fork() はコピー オン ライト ページを使用して実装されるため、発生する唯一のペナルティは、親のページ テーブルを複製し、子に固有のタスク構造を作成するために必要な時間とメモリです。

バージョン 2.3.3 以降、カーネルの fork() システム コールを呼び出すのではなく、NPTL スレッド実装の一部として提供される glibc fork() ラッパーは、従来のシステム コールと同じ効果を提供するフラグを使用して clone(2) を呼び出します。 . ( fork() への呼び出しは、単に SIGCHLD としてフラグを指定する clone(2) への呼び出しと同等です。) glibc ラッパーは、pthread_atfork(3) を使用して確立された fork ハンドラを呼び出します。

そして、クローンのマニュアルページによると:

fork(2) とは異なり、これらの呼び出しにより、子プロセスは実行コンテキストの一部 (メモリ空間、ファイル記述子のテーブル、シグナル ハンドラのテーブルなど) を呼び出しプロセスと共有できます。

だから、私はこれを意味するように読んでいます:ユニコーンがメモリ共有を実装するために依存している機能であるLinuxのフォークコピーオンライトは、libc 2.2.3まで実装されていませんでした(私が間違っていたら誰かが私を修正してください)この解釈では)。

実行している libc のバージョンを確認するには、次のように入力します。

ldd --version

または、glibc を見つけて直接実行します。私のシステムでは、次の場所にファイルが見つかりました。

locate libc.so
/lib/x86_64-linux-gnu/libc.so.6
于 2014-01-05T03:07:38.057 に答える