Linux でのメモリ使用量を正しく判断する

Linux でのメモリ使用量を正しく判断する

私は、私が見ている結果のいくつかに少し混乱しています追伸そして無料

私のサーバーでは、これが結果ですfree -m

[root@server ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          2048       2033         14          0         73       1398
-/+ buffers/cache:        561       1486
Swap:         2047         11       2036

Linux がメモリを管理する方法について私が理解しているのは、ディスク使用量を RAM に保存して、その後のアクセスを高速化するということです。これは「キャッシュ」列で示されていると思います。さらに、さまざまなバッファが RAM に保存され、「バッファ」列で示されます。

したがって、私が正しく理解していれば、「実際の」使用量は「-/+ buffers/cache」の「使用」値、つまりこの場合は 561 になるはずです。

これらすべてが正しいと仮定すると、私を困惑させるのは の結果の部分ですps aux

結果から私が理解しているのはps、6 番目の列 (RSS) は、プロセスがメモリに使用するサイズをキロバイト単位で表しているということです。

したがって、このコマンドを実行すると:

[root@server ~]# ps aux | awk '{sum+=$6} END {print sum / 1024}'
1475.52

結果は、「-/+ buffers/cache」の「used」列になるのではないでしょうかfree -m

では、Linux でプロセスのメモリ使用量を適切に判断するにはどうすればよいでしょうか? どうやら私の論理には欠陥があるようです。

答え1

恥ずかしげもなく私の回答をコピー/ペーストしますサーバー障害つい先日 :-)

Linux の仮想メモリ システムはそれほど単純ではありません。すべての RSS フィールドを単純に合計して、usedによって報告される値を取得することはできませんfree。これには多くの理由がありますが、ここでは最も大きな理由をいくつか挙げます。

  • プロセスがフォークすると、親と子の両方に同じ RSS が表示されます。ただし、Linux はコピーオンライトを採用しているため、両方のプロセスが実際に同じメモリを使用しています。プロセスの 1 つがメモリを変更した場合にのみ、メモリが実際に複製されます。
    これにより、free数値は RSS の合計よりも小さくなりますtop

  • RSS 値には共有メモリは含まれません。共有メモリはどのプロセスにも所有されていないため、topRSS には含まれません。
    このため、free数値は RSS の合計よりも大きくなりますtop

数字が合わない理由は他にもたくさんあります。この回答は、メモリ管理は非常に複雑であり、個々の値を加算/減算するだけではメモリ使用量の合計は算出できないという点を強調しているだけです。

答え2

メモリの数字を合計したものを探しているなら、スメム:

smem は、Linux システムのメモリ使用量に関するさまざまなレポートを提供できるツールです。既存のツールとは異なり、smem は比例セット サイズ (PSS) をレポートできます。これは、仮想メモリ システム内のライブラリとアプリケーションによって使用されるメモリの量をよりわかりやすく表すものです。

通常、物理メモリの大部分は複数のアプリケーション間で共有されるため、常駐セット サイズ (RSS) と呼ばれるメモリ使用量の標準的な測定方法では、メモリ使用量が大幅に過大評価されます。代わりに、PSS では、各共有領域の各アプリケーションの「公平なシェア」を測定して、現実的な測定値を提供します。

たとえば次のようになります:

# smem -t
  PID User     Command                         Swap      USS      PSS      RSS
...
10593 root     /usr/lib/chromium-browser/c        0    22868    26439    49364 
11500 root     /usr/lib/chromium-browser/c        0    22612    26486    49732 
10474 browser  /usr/lib/chromium-browser/c        0    39232    43806    61560 
 7777 user     /usr/lib/thunderbird/thunde        0    89652    91118   102756 
-------------------------------------------------------------------------------
  118 4                                       40364   594228   653873  1153092 

PSSここで興味深い列は、共有メモリを考慮に入れている点です。それを合計することに意味がある
わけではありません。ここでは、ユーザーランド プロセスの合計は 654 MB になります。RSS

システム全体の出力は残りの部分について伝えます:

# smem -tw
Area                           Used      Cache   Noncache 
firmware/hardware                 0          0          0 
kernel image                      0          0          0 
kernel dynamic memory        345784     297092      48692 
userspace memory             654056     181076     472980 
free memory                   15828      15828          0 
----------------------------------------------------------
                            1015668     493996     521672 

つまり、1Gb RAM 合計= 654Mbユーザーランド プロセス+ 346Mbカーネル メモリ+ 16Mb空き容量
(数 MB の誤差あり)

全体としてメモリの約半分がキャッシュに使用されます (494Mb)。

ボーナス質問: ここでのユーザーランド キャッシュとカーネル キャッシュとは何ですか?


ちなみに、視覚的なものとしては以下をお試しください:

# smem  --pie=name

ここに画像の説明を入力してください

答え3

本当に優れたツールは、pmap特定のプロセスの現在のメモリ使用量をリストするものです。

pmap -d PID

詳細については、manページman pmapを参照してください。また、すべてのシステム管理者が知っておくべき 20 の Linux システム監視ツール、これは私が Linux ボックスに関する情報を取得するためにいつも使用する優れたツールをリストします。

答え4

他の人が正しく指摘しているように、共有領域や mmap されたファイルなどがあるため、プロセスによって使用される実際のメモリを把握するのは困難です。

実験者なら、実行することができますヴァルグリンドとマシフこれは一般ユーザーにとっては少々重すぎるかもしれませんが、時間の経過とともにアプリケーションのメモリ動作がどう変化するかを把握できます。アプリケーションが malloc() をまさに必要とするものに使用すれば、プロセスの実際の動的メモリ使用量を適切に表すことができます。ただし、この実験は「有害」になる可能性があります。

事態を複雑にしているのは、Linuxではオーバーコミットメモリ。メモリを malloc() すると、メモリを消費する意図を表明することになります。しかし、割り当ては、割り当てられた「RAM」の新しいページに 1 バイトを書き込むまで実際には行われません。次のような小さな C プログラムを書いて実行することで、これを自分で証明できます。

// test.c
#include <malloc.h>
#include <stdio.h>
#include <unistd.h>
int main() {
    void *p;
    sleep(5)
    p = malloc(16ULL*1024*1024*1024);
    printf("p = %p\n", p);
    sleep(30);
    return 0;
}

# Shell:
cc test.c -o test && ./test &
top -p $!

これを 16 GB 未満の RAM を搭載したマシンで実行すると、なんと 16 GB のメモリを獲得できます (いや、実際はそうではありません)。

top「VIRT」は16.004Gと表示されますが、%MEMは0.0です。

これをもう一度 valgrind で実行します。

# Shell:
valgrind --tool=massif ./test &
sleep 36
ms_print massif.out.$! | head -n 30

massif は「すべての allocs() の合計 = 16GB」と言っています。だから、あまり面白くありません。

しかし、それを正気プロセス:

# Shell:
rm test test.o
valgrind --tool=massif cc test.c -o test &
sleep 3
ms_print massif.out.$! | head -n 30

--------------------------------------------------------------------------------
Command:            cc test.c -o test
Massif arguments:   (none)
ms_print arguments: massif.out.23988
--------------------------------------------------------------------------------


    KB
77.33^                                                                       :
     |                                                                      #:
     |                                                                :@::@:#:
     |                                                           :::::@@::@:#:
     |                                                         @:: :::@@::@:#:
     |                                                     ::::@:: :::@@::@:#:
     |                                             ::@:::@:::::@:: :::@@::@:#:
     |                                            @::@:::@:::::@:: :::@@::@:#:
     |                                            @::@:::@:::::@:: :::@@::@:#:
     |                      :@@@@@@@@@@@@@@@@@@@@:@::@:::@:::::@:: :::@@::@:#:
     |                      :@@                  :@::@:::@:::::@:: :::@@::@:#:
     |                    :@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |                    :@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |                   :@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |                   :@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |              :@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |          :::::@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |        :::::::@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |       ::::::::@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |       ::::::::@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
   0 +----------------------------------------------------------------------->Mi
     0                                                                   1.140

そして、ここでは(非常に経験的に、そして非常に高い信頼性で)コンパイラが 77KB のヒープ領域を割り当てたことがわかります。

なぜヒープ使用量だけを取得するためにそんなに努力するのでしょうか? プロセスが使用する共有オブジェクトとテキスト セクション (この例では、コンパイラ) は、それほど興味深いものではないからです。これらはプロセスにとって一定のオーバーヘッドです。実際、プロセスのその後の呼び出しは、ほとんど「無料」で行われます。

また、次の点を比較対照してください。

1GB のファイルを MMAP() します。VMSize は 1+GB になります。ただし、Resident Set Size は、(その領域へのポインタを逆参照することによって) ページインされたファイルの部分のみになります。ファイル全体を「読み取る」と、最後に到達するまでに、カーネルは既に先頭をページアウトしている可能性があります (カーネルは、再度逆参照された場合にそれらのページを置き換える方法と場所を正確に知っているため、これは簡単に実行できます)。いずれの場合も、VMSize も RSS もメモリの「使用状況」を示す適切な指標にはなりません。実際には何も malloc() していません。

対照的に、Malloc() は、メモリがディスクにスワップされるまで、大量のメモリにアクセスします。そのため、割り当てられたメモリが RSS を超えています。ここで、VMSize が何かを伝え始める可能性があります (プロセスは、RAM に実際に存在するメモリよりも多くのメモリを所有しています)。ただし、共有ページの VM とスワップされたデータの VM を区別するのは依然として困難です。

ここでvalgrind/massifが面白い。意図的に割り当てられます (ページの状態に関係なく)。

関連情報