5

Javaのメモリ消費に関して興味深い問題があります。Javaアプリケーションを呼び出すネイティブC++アプリケーションがあります。

アプリケーションは基本的にいくつかの言語翻訳を行い、いくつかのXMLを解析し、ネットワーク要求に応答します。アプリケーションの状態のほとんどは保持する必要がないため、文字列引数を取り込んで文字列の結果を返すメソッドでいっぱいです。

このアプリケーションは時間とともにますます多くのメモリを使用し続け、2 GB近くのメモリを使用し始める時期が来ています。これにより、一部のハッシュテーブルまたは静的変数のどこかにリークがあると思われます。よく調べてみると、漏れは見つかりませんでした。ある期間のヒープダンプを比較すると、char[]オブジェクトとStringオブジェクトが大量のメモリを消費していることがわかります。

ただし、これらのchar []、文字列を調べると、GCルートがないことがわかります。これは、リークの原因ではないことを意味します。それらはヒープの一部であるため、ガベージコレクションを待機していることを意味します。さまざまなツールMAT\VisualVM \ JHatを使用し、そのようなオブジェクトを多数スクロールした後、yourkitの試用版を使用しました。Yourkitは、char []とStringの96%が到達不能であるとすぐにデータを提供します。これは、ダンプを取得した時点で、ヒープ内の文字列の96%がガベージコレクションを待機していたことを意味します。

GCは控えめに実行されていることを理解していますが、VisualVMで確認すると、実際に実行されていることがわかります:-(どうしてかというと、ヒープ上に未使用のオブジェクトが常にたくさんあるのです。

IMOこのアプリケーションは、最初の24時間はそこにとどまりますが、ヒープを増やし続けるよりも、400〜500MBを超えるメモリを使用することはできません:-(

Java1.6.0-25を実行しています。

yourkitのスクリーンショットに注意してください

助けてくれてありがとう。

4

5 に答える 5

7

Javaは、そうすべきであると思うときにGCを実行しません:-) GCは複雑すぎるトピックであるため、詳細を実際に掘り下げるのに2週間も費やさずに何が起こっているのかを理解できません。したがって、説明できない動作が見られたとしても、それが壊れているという意味ではありません。

あなたが見るものにはいくつかの理由があります:

  1. 巨大な文字列をメモリにロードし、サブ文字列への参照を保持しています。これにより、文字列全体をメモリに保持できます(Javaは、サブ文字列に常に新しいchar配列を割り当てるとは限りません。文字列は不変であるため、元のchar配列を再利用し、オフセットと長さを記憶します)。

  2. これまでのところ、GCをトリガーするものはありません。一部のC++開発者は、GCが「悪」であると信じているため(理解できないものはすべて悪である必要があります)、どうしても必要な場合を除いて、Javaを実行しないように構成します。これは、VMが最大値に達するまでメモリを消費し、その後、1回の巨大なGC実行を実行することを意味します。

  3. ビルド25はすでにかなり古いです。最新のJavaビルドに更新してみてください(33だと思います)。GCはVMで最もよくテストされた部分の1つですが、バグがあります。多分あなたは1つを打った。

  4. OutOfMemoryExceptionが表示されない限り、リークは発生しません。与えられたすべてのヒープを食べるアプリケーションがあります。16GBのRAM(「念のため」)を取得した場合、可能な限りキャッシュするため、16GB全体が使用されます。キャッシュは必要に応じて縮小するため、メモリが不足することはありませんが、システム管理者は日常的に「おやおや!おやおや!メモリが不足しています」とびっくりします。PANIKいいえ、そうではありません。Javaから指示がない限り、メモリが不足することはありません。効率的に使っているだけです。

  5. コマンドラインオプションを使用してGCを調整することは、GCを中断するための最良の方法の1つです。あなたがこれまで以上にトピックについて多くを知っている何百人もの人々は、GCを効率的にするために何年も費やします。あなたはもっとうまくやれると思いますか?幸運を。->「魔法の」コマンドラインオプションとへの呼び出しを取り除くSystem.gc()と、問題が解決する可能性があります。

于 2012-08-02T07:50:51.243 に答える
2

ヒープサイズを500メガバイトに減らして、ソフトウェアがガベージコレクションを開始するか死ぬかを確認してください。Javaは、与えられたメモリの使用についてあまり煩わしくありません。また、GCの調整オプションを調べて、GCのクリーンアップについてより慎重にすることもできます。

于 2012-08-02T07:40:50.657 に答える
2
String reallyLongString = "this is a really long String";
String tinyString = reallyLongString.substring(2, 3);
reallyLongString = null

上記の場合、JVMは長い文字列に割り当てられたメモリを収集できません。これは、その一部への参照があるためです。文字列を使って何かをしているときにメモリの問題に苦しんでいる場合は、これが悲しみの原因である可能性があります。

tinyString = new String(reallyLongString.substring(2, 3);代わりに使用してください。

于 2012-08-02T07:45:58.310 に答える
2

リークはまったくない可能性があります。リークは、Stringが到達可能である場合に発生します。アプリケーションに最大2GBを割り当てた場合、その制限に近づくまで、ガベージコレクターがメモリの解放を開始する理由はありません。500MBを超えないようにする場合は-Xmx 512m、JVMの起動時にパスします。

ガベージコレクターを調整して、はるかに早くクリーンアップを開始することもできます。

于 2012-08-02T07:51:46.543 に答える
0

まず第一に、それらの文字列とchar[]について心配するのをやめてください。私がプロファイリングしたほとんどすべてのJavaアプリケーションで、それらはメモリコンシューマリストの一番上にあります。そして、それらのJavaアプリケーションのほとんどでは、それらは本当の問題ではありませんでした。

OutOfMemoryErrorをまだ受け取っていないが、Javaプロセスには2GBが多すぎることを心配している場合は、渡すXmx値を減らしてみてください。512mまたは1gで問題なく動作する場合は、問題は解決しましたね。

OOMを取得した場合、試すことができるもう1つのオプションは、JavaプロセスでPlumbrを使用することです。これはメモリリーク検出ツールであり、実際にメモリリークがある場合に役立ちます。

于 2012-08-03T06:36:47.070 に答える