web-dev-qa-db-ja.com

OpenJDK JVMがヒープメモリをLinuxに戻すことはありますか?

私たちは長期間のサーバープロセスを使用しているため、しばらくの間、多くのRAMが必要になることはめったにありません。 OS。JVMにヒープメモリをOSに返すように依頼するにはどうすればよいですか。

通常、このような質問に対する受け入れられた回答は、_-XX:MaxHeapFreeRatio_および_-XX:MinHeapFreeRatio_を使用することです。 (例 12 、、 4 を参照)。しかし、Javaのように実行しています:

_Java -Xmx4G -XX:MaxHeapFreeRatio=50 -XX:MinHeapFreeRatio=30 MemoryUsage
_

そして、これはまだVisualVMで見られます:

Visual VM memory usage

明らかに、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 working with simple app

ただし、実際のアプリで_-XX:+UseSerialGC_を試したところ、それほど多くはありませんでした。

-XX:+UseSerialGC not working with real app

しばらくしてdid助けてgc()を見つけたので、多かれ少なかれスレッドを作成しました:

_while (idle() && memoryTooLarge() && ! tooManyAttemptsYet()) {
    Thread.sleep(10*1000);
    System.gc();
}
_

そしてそれはトリックをしました:

GC thread working

私は以前、_-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_を取得して、これらすべてのフープを通過することなく、約束どおりのことを実行することはできませんか?

52
Peter V. Mørch

G1(-XX:+ UseG1GC)、Parallel scavenge(-XX:+ UseParallelGC)、ParallelOld(-XX:+ UseParallelOldGC)は、ヒープの縮小時にメモリを返します。シリアルとCMSについてはよくわかりませんが、私の実験ではヒープは縮小されませんでした。

両方の並列コレクターは、ヒープを「許容可能な」サイズに縮小する前に、いくつかのGCを必要とします。これはデザインごとです。それらは、将来必要になると想定して、意図的にヒープを保持しています。フラグ-XX:GCTimeRatio = 1を設定すると状況が多少改善されますが、大幅に縮小するにはいくつかのGCが必要です。

G1はヒープの高速縮小に非常に優れているため、上記のユースケースでは、すべてのキャッシュやクラスローダーなどを解放した後、G1を使用してSystem.gc()を実行することで解決できると言えます。

http://bugs.Java.com/bugdatabase/view_bug.do?bug_id=6498735

7
M.Z