JDK 1.6 で実行されている Java アプリケーションでヒープ サイズの使用をテストしています。ツール VisualVM を使用して、ヒープの使用状況を監視します。数分間、最大ヒープ サイズの使用量が約 500 MB であることがわかりました。System.gc() を呼び出すオプション「Perform GC」を使用しました。初めて使用したとき、最大ヒープは 410MB に減少し、もう一度使用して 130MB を取得し、次回は 85MB にしました。間隔を空けずに、次から次へと4回すべての呼び出しを行いました。System.gc() の呼び出しで最初にすべてのヒープが 85MB に収集されないのはなぜですか。この背後に他の理由がありますか。または、他の方法を試す必要がありますか?
3 に答える
System.gc() は、すべてのオブジェクトが 1 回スキャンされると戻ります。
オブジェクトは、収集後に finalized() する必要があります。ほとんどのオブジェクトはこのメソッドを実装していませんが、実装しているオブジェクトについては、後でクリーンアップするためにキューに追加されます。これは、これらのオブジェクトをまだクリーンアップできないことを意味します (オブジェクトを保持するキューノードではありません)。つまり、GC をトリガーする行為により、メモリ消費が一時的に増加する可能性があります。
さらに、GC によってクリーンアップされる場合とされない場合があるオブジェクトへの SoftReferences があります。他の多くがクリーンアップされていない場合にのみ、これらをクリーンアップする必要があるという前提があります。
つまり、すべてのオブジェクトを 1 サイクルでクリーンアップできるわけではありません。
考えられる理由の 1 つは、java.lang.ref.Reference
型の使用である可能性があります。GC が「参照」を壊そうとする場合、これは GC が適切に完了した後に発生します。結果として到達不能になったオブジェクトは、次の GC サイクルで処理するために残されます。
ファイナライズも同じように機能します。オブジェクトにファイナライズが必要な場合、そのオブジェクトとそこから到達可能なすべてのオブジェクト (のみ) は、次の GC サイクルでのみ収集可能になる可能性があります。
次に、ヒープを縮小するための GC のアルゴリズムが非積極的であるという問題があります。Java HotSpot VM Optionsページによると、ガベージ コレクション後に 70% 以上が空いている場合にのみ、GC はヒープを縮小します。ただし、これが完全な GC を指しているかどうかは完全には明らかではありません。したがって、GC で部分的な GC を実行して縮小し、次に完全な GC でさらに縮小することができます。
(Javadoc の文言から、System.gc()
完全な GC を実行すると推測する人もいます。ただし、これは実際にはバージョン/GC に依存していると思われます。)
しかし、正直に言うと、これはすべて議論の余地があるはずです。できるだけ多くのメモリを返すようにアプリケーションを強制しようとしても無意味です。キャッシュされたデータを破棄するように強制している可能性があります。アプリケーションが再びアクティブになると、キャッシュのリロードが開始されます。