『Javaラック構築ベース』Javaベースから――IntとIntegerの深い分析


1.intとIntegerの問題区別分析について
  • コンパイルフェーズ、実行時、自動梱包/自動解体はどのフェーズで発生しますか?
  • 静的ファクトリメソッドvalueOfを使用するとキャッシュメカニズムが使用されますが、自動梱包時にキャッシュメカニズムが機能しますか?
  • なぜ元のデータ型が必要なのか、Javaのオブジェクトも効率的なようですが、アプリケーションでどのような違いが発生しているのでしょうか.
  • Integerソースを読んだことがありますか?次のクラスまたはいくつかの方法の設計要点を分析しますか?
  • intとIntegerの違い
  • 1、Integer int    ,int  java          
    2、Integer            , int      
    3、Integer        , new  Integer ,               ; int          
    4、Integer     null,int     0
    
      : 
      Integer int    
    1、  Integer         Integer     ,      new   Integer         (  new        ,       )。
    
    Integer i = new Integer(100);
    Integer j = new Integer(100);
    System.out.print(i == j); //false
    
    2、Integer   int     ,            ,    true(     Integer       int   ,java       int,      ,        int     )
    
    Integer i = new Integer(100);
    int j = 100;
    System.out.print(i == j); //true
    
    3、 new   Integer   new Integer()        ,   false。(   new   Integer      java       , new Integer()              ,           )
    
    Integer i = new Integer(100);
    Integer j = 100;
    System.out.print(i == j); //false
    
    4、     new   Integer  ,     ,           -128 127  ,      true,             ,      false
    
    Integer i = 100;
    Integer j = 100;
    System.out.print(i == j); //true
    
    Integer i = 128;
    Integer j = 128;
    System.out.print(i == j); //false
    
       4    : 
    java   Integer i = 100 ; ,     Integer i = Integer.valueOf(100);, java API  Integer   valueOf     :
    
    public static Integer valueOf(int i){
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high){
            return IntegerCache.cache[i + (-IntegerCache.low)];
        }
        return new Integer(i);
    }
    
    java  -128 127    ,     ,Integer i = 127 ,  127    ,    Integer j = 127 ,         ,   new 

    2.Integerの値キャッシュの原理
    2.1 Java 5でのキャッシュ機能の導入
    Java 5では、Integerの操作に新しい機能を導入し、メモリを節約し、パフォーマンスを向上させます.整数オブジェクトは、内部インプリメンテーションで同じオブジェクトリファレンスを使用してキャッシュと再利用を実現します.
    このIntegerキャッシュポリシーは自動箱詰め(autoboxing)の場合にのみ有用であり、コンストラクタを使用して作成したIntegerオブジェクトはキャッシュできません.
    2.2 IntegerクラスのIntegerCacheクラス
    新しいIntegerオブジェクトを作成する前にIntegerCache.Cache(Integerタイプの配列)で検索します.Integerのキャッシュを担当するJavaクラスがあります.
    このクラスは、キャッシュサポートを実現し、−128〜127の間の自動梱包プロセスをサポートするために使用される.最大127は、JVMの起動パラメータ-XX:AutoBoxCacheMax=sizeで変更できます.キャッシュはforサイクルで実現されます.小さいものから大きいものまでできるだけ多くの整数を作成し、cacheという整数配列に格納します.このキャッシュはIntegerクラスが初めて使用されたときに初期化されます.その後、新しいインスタンスを作成するのではなく、キャッシュに含まれるインスタンスオブジェクトを使用できます(自動梱包の場合).
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
    
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
    
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
    
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
    
        private IntegerCache() {}
    }

    2.3その他のフルタイプのキャッシュメカニズム
    このキャッシュ動作はIntegerオブジェクトだけではありません.すべての整数タイプのクラスに対して同様のキャッシュメカニズムがあります.
  • ByteCacheは、Byteオブジェクト
  • をキャッシュするために用いる.
  • ShortCache Shortオブジェクトをキャッシュするための
  • がある.
  • LongCacheは、Longオブジェクト
  • をキャッシュするために使用する.
  • CharacterCacheは、Characterオブジェクト
  • をキャッシュするために使用する.
  • Byte,Short,Longには固定範囲:-128から127がある.Characterの場合、範囲は0~127です.Integerがパラメータで範囲を変えることができる以外はだめです.

  • 3.自動梱包と解体の理解
    3.1梱包とは何ですか.分解箱とは何ですか.
    梱包とは、基本データ型を自動的に梱包器タイプに変換することである.ボックスの取り外しは、パッケージタイプを基本データ型に自動的に変換します.
    //  
    int yc = 5;
    //  
    Integer yc = 5;
    3.2            
     Interger   ,                  
    public class Main {
        public static void main(String[] args) {
            Integer y = 10;
            int c = i;
        }
    }

    次にコンパイルして、次のようにします.
  • 逆コンパイルで得られたバイトコードの内容から分かるように,箱詰め時に自動的に呼び出されるのがIntegerのvalueOf(int)メソッドである.ボックスを外すときに自動的に呼び出されるのがIntegerのintValueメソッドです.
  • したがって、梱包と解体の実装プロセスは、梱包のvalueOfメソッドを呼び出すことによって実装され、解体プロセスは、梱包のxxxValueメソッドを呼び出すことによって実装される、一言でまとめることができる.

  • 3.3梱包と解体のプログラミングの実際における注意点
    特に、パフォーマンスに敏感な場合、10万個のJavaオブジェクトと10万個の整数のオーバーヘッドを作成することは、メモリの使用にかかわらず、処理速度にかかわらず、オブジェクトヘッダのスペース占有量だけで数の差になることをお勧めします.
    4.元のタイプのスレッドセキュリティの問題
    4.1スレッドが安全なタイプ
    Javaが持つスレッドセキュリティの基本タイプは、AtomicInteger、AtomicLong、AtomicBoolean、AtomicIntegerArray、AtomicLongArrayなどです.
    4.2 intタイプがスレッドセキュリティであるかどうかを検証する方法
    200スレッド、共有変数countごとに50回++操作
    intは基本タイプとしてメモリスタックに直接格納され、+、-操作および++、-操作は原子操作ではなく、他のスレッドによって切断される可能性があるため、スレッドセキュリティではありません.int単一スレッド変数アクセス用、オーバーヘッドが小さく、速度が速い
    int count = 0;
    private void startThread() {
        for (int i = 0;i < 200; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int k = 0; k < 50; k++){
                        count++;
                    }
                }
            }).start();
        }
        //   10 ,         
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            Log.e("    ----",count+"");
        }
    }
    
    //    10000,      9818
    //  :    ----: 9818

    4.3 AtomicIntegerスレッドセキュリティ版
    AtomicIntegerクラスには、AtomicIntegerクラスのvalueのメモリ位置を記述する変数valueOffsetがあります.
    変数の値を変更する必要がある場合は、get()によってvalueOffset位置の値、すなわち現在のvalueの値を得る.この値を増加させ、nextに与える
    compareAndSet()は,以前に取ったvalueの値が現在変化しているかどうかを比較し,変化していなければnextの値をvalueに与え,以前の値と比較して変化すればアクセスが成功するまで再ループすることで,この変数がスレッドの安全性を保証できる.
    valueはvolatileキーワードを使用し、複数のスレッドが変数を共有できるようにし、volatileを使用するとVM最適化が機能しなくなり、オンライン・スレッド数が特に大きい場合、効率が低下します.
    private static AtomicInteger atomicInteger = new AtomicInteger(1);
    static Integer count1 = Integer.valueOf(0);
    private void startThread1() {
        for (int i = 0;i < 200; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int k = 0; k < 50; k++){
                        // getAndIncrement:     ,   1,         
                        count1 = atomicInteger.getAndIncrement();
                    }
                }
            }).start();
        }
        //   10 ,         
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            Log.e("    ----",count1+"");
        }
    }
    
    //    10000,      10000
    //  :    ----: 10000
    
    //AtomicInteger   volatile       ,            。
    private volatile int value;
    /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    5.Java元データ型と参照型の制限
    5.1元データ型とJava汎用型は併用できない
    Javaの汎用型はある程度擬似汎用型とすることができ、完全にコンパイル期間のテクニックであり、Javaコンパイル期間は自動的にタイプを対応する特定のタイプに変換し、汎用型を使用することを決定し、対応するタイプがObjectに変換できることを保証しなければならない.
    5.2データを効率的に表現することができず、複雑なデータ構造を表現することも容易ではない
    Javaのオブジェクトはすべて参照タイプで、元のデータ型配列であれば、メモリには連続したメモリであり、オブジェクト配列ではなく、データが格納されているのは参照であり、オブジェクトはスタックの異なる位置に分散して格納されることが多い.この設計は極めて柔軟性をもたらしたが,特に現代のCPUキャッシュメカニズムを十分に利用できないデータ操作の低効率化をもたらした.
    Javaはオブジェクト内に様々なマルチステート,スレッドセキュリティなどのサポートを構築しているが,これはすべての場合のニーズではなく,特にデータ処理の重要性が日増しに高まり,より高密度な値タイプが非常に現実的なニーズである.
    6.その他の知識の延長について
    6.1オブジェクトのメモリ構造
    メモリに格納されたオブジェクトのレイアウトは、オブジェクトヘッダ(Header)、インスタンスデータ(Instance Data)、およびPaddingの3つの領域に分けられます.
    6.2オブジェクトヘッダの構造
    HotSpot仮想マシンのオブジェクトヘッダには、オブジェクト自体のランタイムデータを格納するための2つの部分情報が含まれています.ハッシュコード(HashCode)、GC世代別年齢、ロックステータスフラグ、スレッドが持つロック、バイアススレッドID、バイアスタイムスタンプなど、32ビットと64ビットの仮想マシン(圧縮ポインタを開かない)の長さはそれぞれ32 bitと64 bitであり、公式には「Mark Word」と呼ばれている.
    オブジェクトヘッダの他の部分はタイプポインタ、すなわちオブジェクトがそのクラスメタデータを指すポインタであり、仮想マシンはこのポインタによってこのオブジェクトがどのクラスのインスタンスであるかを決定する.すべての仮想マシン実装がオブジェクトデータにタイプポインタを保持する必要があるわけではない、すなわち、オブジェクトを検索するメタデータ情報がオブジェクト自体を通過する必要はないという点で2.3になる.3節討論.また、オブジェクトがJava配列である場合、仮想マシンは通常のJavaオブジェクトのメタデータ情報でJavaオブジェクトのサイズを決定できるが、配列のメタデータから配列のサイズを決定できないため、オブジェクトヘッダに配列長を記録するためのデータが必要である.
    6.3 Javaオブジェクトのサイズを計算または取得する方法
    JAVAオブジェクトのサイズを取得し、オブジェクトをバイナリのByteにシーケンス化してサイズを表示できます.
    //    JAVA     ,                 Byte,       
    Integer value = 10;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos ;
    try {
        oos = new ObjectOutputStream(bos);
        oos.writeObject(value);
        oos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    //              
    Log.e("    ----",bos.size()+"");
    //    ----: 81