Don’t Cross 32 GB!


elasticsearchの公式ドキュメントを見ると、Don't Cross 32 GBという観点があります.JVMスタックが32 G未満の場合、HotSpot仮想機会は圧縮オブジェクトポインタを有効にするためです.32 Gを超えると、この圧縮オブジェクトポインタは無効になります.では、果たしてこの臨界値の正確な値はどれほど大きいのだろうか.圧縮ポインタをオンにするよりもオンにしないほうが、どれだけのメモリを節約できますか?真相を探ってみましょう.
Don’t Cross 32 GB!
Javaの世界では、ほとんどのオブジェクトがスタックに割り当てられ、ポインタで参照されます(Student stu=new Student()newのこのStudentオブジェクトはスタックに割り当てられ、stuはこのオブジェクトを持つアプリケーションです).
32ビットのオペレーティングシステム、最大4 Gメモリのみサポート(すなわち2^32).当然ながら、32ビットサーバは絶滅しているはずなので、本稿では64ビットオペレーティングシステムについて論じる.64ビットオペレーティングシステムにとって、理論的に割り当てられたスタックは非常に大きいことができる.しかし、64ビットポインタのオーバーヘッドは、ポインタが大きいだけでなく、より多くの無駄な空間があることを意味する.無駄な空間よりも悪いのは、64ビットポインタのオーバーヘッドであるプライマリ・メモリとマルチレベル・キャッシュの間でデータを移動する場合、より多くの帯域幅が消費されます.
Javaは「compressed oops」術でこの問題を解決し、ポインタはメモリの正確な位置を指すのではなく、オブジェクトのオフセット量(原文:Instead of pointing at exact byte locations in memory,the pointers reference object offsets)です.これは、合計2^32バイトの大きなオブジェクトを参照するのではなく、32ビットポインタが2^32オブジェクト(約43億オブジェクト)を参照できることを意味します.したがって、スタックサイズは32 G程度まで32ビットポインタを保持することができます.
この32 G --を越えると魔法のような数値になります.ポインタは通常のオブジェクトポインタに戻ります.各ポインタが大きくなると、より多くのCPU、メモリ、帯域幅が必要になり、本当にオブジェクトを維持するためのメモリが少なくなります.これにより、compressed oopsを使用する32 Gのスタックと、40~50 Gでcompressed oopsを使用していないスタックで保存されているオブジェクトの数が同じになるという奇妙な現象が生じる可能性がある.
この事実は、余分なメモリがあっても、32 Gを超える限界をできるだけ避けるべきだと教えています.メモリを浪費し、CPUの性能を低下させ、多くの場合GCの表現も一般的です.更に面倒なのは、そんなに大きい山、DUMP分析は1件のきわめて苦痛で、きわめて面倒な事です.私を信じて、あなたはきっとそのような局面に遭遇したくないに違いない.
どのくらいの大きさに設定すればいいですか?
32 Gは近似値であり,この臨界値はJVMとプラットフォームに関係している.正確に設定したくない場合は、31 Gは安全を決定する数値であり、31 Gはcompressed oopsをデフォルトでオンにするに違いない.JVMパラメータ-XX:+PrintFlagsFinalを増やし、UseCompressedOopsの値を検証することで、圧縮ポインタが本当にオンになったのか、それとも圧縮ポインタが無効になったのかを知ることができます.
実践こそ真理を検証する唯一の基準である.JUST DO IT!この臨界値を検証しましょう.
検証状況-- JDK 8を前提として、32760 mのスタックは圧縮ポインタを開き、32770 mのスタック圧縮ポインタはすでに閉じている.
[afei@afei ~]$ java -version
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

[afei@afei ~]$ java -Xmx32760m -XX:+PrintFlagsFinal 2> /dev/null | grep UseCompressedOops
     bool UseCompressedOops                        := true                                {lp64_product}
[afei@afei ~]$ java -Xmx32770m -XX:+PrintFlagsFinal 2> /dev/null | grep UseCompressedOops
     bool UseCompressedOops                         = false                               {lp64_product}

32 G、すなわち32*1024=32768 Mは、ちょうど[32760,32770]の範囲にある.
土豪私は1 Tメモリを持っています
JVMの多くの欠点が多すぎます.
  • は32 G圧縮ポインタを超えて失効した.
  • DUMP分析は災害です.
  • スタックが大きいほど、GCの表現は悪くなります.

  • とにかく、このカニを食べてみないで!もし私がお金があってわがままな会社だったら、サーバーの牛は要らないで、すべて128 Gからスタートします!結局貧乏に想像を制限させないでください!
    この場合、32 GのJVMインスタンスを複数開くことをお勧めします.4つの32 GのJVMインスタンスは、1つの128 Gインスタンスよりも絶対的に優れている.
    単純なテスト検証
    筆者は簡単なテストを行い、この問題を検証した:割当設定XmxはXmx32760mXmx32770mであり、Xmnはいずれも100 Mである(S 0:S 1:Edenデフォルト1:1:8).合計で8個のオブジェクトタイプと8個の原子タイプおよびString、合計17個のタイプ属性を含むオブジェクト1 kw回を割り当てる.それぞれどれだけYGCがトリガーされたかを見て、結論は以下の表の通りである.
    じっけんかいすう
    圧縮ポインタYGCオン回数
    圧縮ポインタYGCを閉じる回数
    1
    17.53
    21.91
    2
    17.54
    21.92
    3
    17.52
    21.94
    実行結果から,Young領域が全く同じ場合,圧縮ポインタを開くよりも圧縮ポインタを閉じる方が20%以上のメモリを節約できることが分かった.このことから、32 Gは本当に奇妙な魔法の数値であることがわかります!なお、YGC回数は小数点以下であり、Eden領域占有率を示すものであり、例えば17.52回YGCでは17回YGCが発生し、Edenでは52%を占めている.
    実行するコマンド:java -verbose:gc -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xmx32770m -Xms32770m -Xmn100m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly Test
    説明:-Xmx32760m -Xms32760mは圧縮ポインタを開くことを示す.-Xmx32770m -Xms32770mは圧縮ポインタを閉じることを示す.
  • 添付:テストソース
  • /**
     * @author wangzhenfei
     * @date 2018-09-05
     * @since 1.0.0
     */
    class Student{
        private byte a;
        private short b;
        private int c;
        private long d;
        private float e;
        private double f;
        private boolean g;
        private char h;
    
        private Byte i;
        private Short j;
        private Integer k;
        private Long l;
        private Float m;
        private Double n;
        private Boolean o;
        private Character p;
    
        private String q;
    
        //          
    }
    public class Test {
    
        public static void main(String[] args) throws Exception{
    
            for (int i = 0; i < 10000000; i++) {
                Student stu = new Student(
                        (byte)(i%128), (short)(i%256), i, i, i, i, i%2==0?true:false, 'a',
                        (byte)(i%128), (short)(i%256), i, (long)i, (float)i, (double)i, 
                        i%2==0?Boolean.TRUE:Boolean.FALSE, 'a', String.valueOf(i));
                if(i>0 && i%100000==0){
                    System.out.println("i="+i);
                }
            }
            //       GC  
            Thread.sleep(20000);
        }
    }
    

    参照先:https://www.elastic.co/guide/en/elasticsearch/guide/current/heap-sizing.html