私たちは長期間のサーバープロセスを使用しているため、しばらくの間、多くのRAMが必要になることはめったにありません。 OS。JVMにヒープメモリをOSに返すように依頼するにはどうすればよいですか。
通常、このような質問に対する受け入れられた回答は、_-XX:MaxHeapFreeRatio
_および_-XX:MinHeapFreeRatio
_を使用することです。 (例 1 、 2 、、 4 を参照)。しかし、Javaのように実行しています:
_Java -Xmx4G -XX:MaxHeapFreeRatio=50 -XX:MinHeapFreeRatio=30 MemoryUsage
_
そして、これはまだVisualVMで見られます:
明らかに、JVMは_-XX:MaxHeapFreeRatio=50
_を尊重していません。これは、heapFreeRatioが100%に非常に近く、50%に近くないためです。 「Perform GC」をクリックしても、メモリはOSに戻りません。
MemoryUsage.Java:
_import Java.util.ArrayList;
import Java.util.List;
public class MemoryUsage {
public static void main(String[] args) throws InterruptedException {
System.out.println("Sleeping before allocating memory");
Thread.sleep(10*1000);
System.out.println("Allocating/growing memory");
List<Long> list = new ArrayList<>();
// Experimentally determined factor. This gives approximately 1750 MB
// memory in our installation.
long realGrowN = 166608000; //
for (int i = 0 ; i < realGrowN ; i++) {
list.add(23L);
}
System.out.println("Memory allocated/grown - sleeping before Garbage collecting");
Thread.sleep(10*1000);
list = null;
System.gc();
System.out.println("Garbage collected - sleeping forever");
while (true) {
Thread.sleep(1*1000);
}
}
}
_
バージョン:
_> Java -version
openjdk version "1.8.0_66-internal"
OpenJDK Runtime Environment (build 1.8.0_66-internal-b01)
OpenJDK 64-Bit Server VM (build 25.66-b01, mixed mode)
> uname -a
Linux londo 3.16.0-4-AMD64 #1 SMP Debian 3.16.7-ckt11-1+deb8u5 (2015-10-09) x86_64 GNU/Linux
> lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 8.2 (jessie)
Release: 8.2
Codename: jessie
_
OpenJDK 1.7とSun Javaの1.8も試しました。すべてが同じように動作し、OSにメモリを戻すものはありません。
私はこれが必要だと思いますが、ディスクIO 2GBガベージに近いページングに出入りするのに費やすことは、リソースの無駄遣いなので、スワップとページングはこれを「解決」しません。同意しないでください、私を啓発してください。
また、malloc()
/free()
を使用して小さなmemoryUsage.cを作成しましたが、doesはメモリを返しますOSに。したがって、Cでは可能です。おそらくJavaではないのでしょうか。
編集:アウグストは、検索によって_-XX:MaxHeapFreeRatio
_に導いた可能性があり、_-XX:MinHeapFreeRatio
_は_-XX:+UseSerialGC
_でのみ機能することを指摘しました。私は有頂天になってそれを試してみましたが、自分でこれを見つけられなかったのではないかと困惑しました。はい、それは私のMemoryUsage.Javaで動作しました:
ただし、実際のアプリで_-XX:+UseSerialGC
_を試したところ、それほど多くはありませんでした。
しばらくしてdid助けてgc()を見つけたので、多かれ少なかれスレッドを作成しました:
_while (idle() && memoryTooLarge() && ! tooManyAttemptsYet()) {
Thread.sleep(10*1000);
System.gc();
}
_
そしてそれはトリックをしました:
私は以前、_-XX:+UseSerialGC
_と複数のSystem.gc()
呼び出しの動作をいくつかの実験で実際に見たことがありますが、GCスレッドの必要性は好きではありませんでした。そして、それが私たちのアプリとJavaの両方の進化に応じて引き続き機能するかどうかを知っています。もっと良い方法があるはずです。
System.gc()
を4回(すぐにではなく)呼び出すように強制するロジックは何ですか。また、これはどこに文書化されていますか?
_-XX:MaxHeapFreeRatio
_および_-XX:MinHeapFreeRatio
_のドキュメンテーションを検索して、_-XX:+UseSerialGC
_でのみ機能する Java tool/executable =また、_-XX:MaxHeapFreeRatio
_および_-XX:MinHeapFreeRatio
_が_-XX:+UseSerialGC
_でのみ機能することについてはどこにも言及されていません。実際、修正された問題 [JDK-8028391] Min/MaxHeapFreeRatioフラグを作成します管理可能 言う:
アプリケーションが多かれ少なかれGCを可能にする方法とタイミングを制御できるようにするには、フラグ-XX:MinHeapFreeRatioと-XX:MaxHeapFreeRatioを管理可能にする必要があります。これらのフラグのサポートは、デフォルトの並列コレクターにも実装する必要があります。
修正された問題の コメント は次のように述べています:
これらのフラグのサポートは、適応サイズポリシーの一部としてParallelGCにも追加されています。
私が確認したところ、 patch で参照されている openjdk-8にバックポートされた修正済みの問題 は、openjdkの ソースパッケージtarball に含まれています。使用している-8バージョン。したがって、「デフォルトの並列コレクター」では明らかに機能するはずですが、この投稿で説明したようには機能しません。 _-XX:+UseSerialGC
_でのみ機能することを示すドキュメントはまだ見つかりません。そして、私がここで文書化したように、これでさえ信頼できず、危険です。
_-XX:MaxHeapFreeRatio
_と_-XX:MinHeapFreeRatio
_を取得して、これらすべてのフープを通過することなく、約束どおりのことを実行することはできませんか?
G1(-XX:+ UseG1GC)、Parallel scavenge(-XX:+ UseParallelGC)、ParallelOld(-XX:+ UseParallelOldGC)は、ヒープの縮小時にメモリを返します。シリアルとCMSについてはよくわかりませんが、私の実験ではヒープは縮小されませんでした。
両方の並列コレクターは、ヒープを「許容可能な」サイズに縮小する前に、いくつかのGCを必要とします。これはデザインごとです。それらは、将来必要になると想定して、意図的にヒープを保持しています。フラグ-XX:GCTimeRatio = 1を設定すると状況が多少改善されますが、大幅に縮小するにはいくつかのGCが必要です。
G1はヒープの高速縮小に非常に優れているため、上記のユースケースでは、すべてのキャッシュやクラスローダーなどを解放した後、G1を使用して
System.gc()
を実行することで解決できると言えます。