15

C++ で記述されたサーバー アプリケーションがあります。起動後、x86 Linux (Ubuntu 8.04、GCC 4.2.4) で約 480 KB のメモリを使用します。480 KB は過剰な量のメモリだと思います。サーバーはまだ何も実行しておらず、クライアントもサーバーに接続していません。(480 KB が大量のメモリであると考える理由を説明している以下のコメントも参照してください。) 初期化中にサーバーが行う唯一のことは、1 つまたは 2 つのスレッドの生成、いくつかのソケットのセットアップ、およびその他の単純なことです。非常にメモリ集約的です。

VM サイズではなく、実際のメモリ使用量について話していることに注意してください。アイドル状態のラップトップでサーバーの 100 個のインスタンスを起動し、サーバー インスタンスを起動する前後のシステム メモリの使用量を「空き」で測定することで測定しました。私はすでにファイルシステムのキャッシュなどを考慮に入れています。

いくつかのテストの後、サーバー自体が何もしない場合でも、C++ ランタイムの何かが原因でサーバーがこれだけ多くのメモリを使用しているように見えます。たとえば、挿入すると

getchar(); return 0;

直後の

int main(int argc, char *argv[]) {

その場合、メモリ使用量はインスタンスあたり 410 KB のままです。

私のアプリケーションは Curl と Boost だけに依存しています。私は C プログラミングの経験がかなりあり、C ライブラリを使用するまでは、C ライブラリがメモリ消費を増加させる傾向がないことを知っています。

私が見つけた他のもの:

  • 単純な hello world C アプリは、約 50 KB のメモリを消費します。
  • Curl にリンクされているが Curl を使用していない単純な hello world C アプリも、約 50 KB のメモリを消費します。
  • シンプルな hello world C++ アプリ (Boost なし) は、約 100 KB のメモリを消費します。
  • 一部の Boost ヘッダーを含むが実際には Boost を使用しない単純な hello world C++ アプリは、約 100 KB のメモリを消費します。「nm」を使用して実行可能ファイルを検査すると、Boost シンボルが表示されません。

したがって、私の結論は次のとおりです。

  1. Gcc は未使用の Boost シンボルを破棄します。
  2. アプリが Boost を使用している場合、C++ ランタイムの何か (おそらく動的リンカー) が原因で大量のメモリが使用されます。しかし、何?これらが何であるかを知るにはどうすればよいですか? また、それらについて何ができるでしょうか?

数年前に C++ 動的リンカーの問題について KDE で議論されたことを覚えています。当時の Linux C++ 動的リンカーは、KDE ​​C++ アプリの起動時間を遅くし、大量のメモリを消費していました。私の知る限り、これらの問題は C++ ランタイムで修正されています。しかし、私が目にしている過度のメモリ消費の原因は、似たようなものでしょうか?

gcc/動的リンクの専門家からの回答は大歓迎です。

興味のある方のために、問題のサーバーは Phusion Passenger のロギング エージェントです: https://github.com/FooBarWidget/passenger/blob/master/ext/common/LoggingAgent/Main.cpp

4

3 に答える 3

5

Cランタイムは、プロセスが通常の操作の一部として実際に使用するよりも多くのメモリを割り当てます。これは、カーネルレベルでのメモリの割り当てが非常に遅く、ページサイズのブロックでしか実行できないためです(ページサイズは通常x86ボックスでは4kbですが、それより大きくなることもあり、通常x64マシンでは8kb以上です)。

さらに、Cランタイムは、満たすことができない割り当て要求を受信すると、多くの場合、必要以上に割り当てて、ほとんどの場合カーネルに行く費用を削減します。

最後に、ブーストグッズを使用している場合、それらはおそらく、などのいくつかのSTLコンポーネントに依存しますstd::vector。これらのコンポーネントは、を使用して要素にスペースを割り当てstd::allocator<T>ます。これにより、実際に使用されるよりも多くのスペースが割り当てられる場合があります。(特に、、、のようなノードベースの構造は、std::map通常、リストまたはツリーのノードを同じメモリページにまとめるためにこれを行います)std::setstd::list

簡単に言えば、これについて心配する必要はありません。想像力の範囲では、メモリの半分のメガバイトはそれほど多くはありません(少なくとも今日では)、そしてそのほとんどはおそらく動的割り当て関数の使用を償却しているだけです。実際のサーバーを作成し、それがメモリを使いすぎている場合は、メモリ使用量を減らす方法を検討します。

編集:使用しているブーストのコンポーネントがたまたまasioであり、ソケットを使用している場合は、ソケットのバッファーも維持するために、いくらかのメモリが消費されていることも知っておく必要があります。

于 2010-11-14T18:10:34.417 に答える
1

メモリ消費を減らす1つの方法は、スレッドスタックサイズを減らすことです。

ブーストに関しては、Steve Jessopがコメントしたように、「ブースト」よりももう少し具体的にする必要があります。

于 2010-11-14T18:01:38.187 に答える
1

動的ロード ライブラリの一部でベース アドレスの競合に問題があるようです。ロード中に再配置が必要な場合は、プライベートな固定コピーとしてマップされます。

prelinkシステム全体で再実行します。ライブラリが優先アドレスに読み込まれると、共有メモリとしてマップされ、使用するプロセスの数に関係なく、コードのコピーが 1 つだけかかります。

ところで、prelinkKDEの修正でもありました。

于 2010-11-14T18:25:01.897 に答える