JVMのオブジェクト作成プロセス

5676 ワード

一、Javaオブジェクトの作成プロセス
Javaプログラムでは、通常newキーワードでオブジェクトを作成しますが、仮想マシンでオブジェクトはどのように作成されますか?(通常のJavaオブジェクト、配列とClassオブジェクトを含まない)
仮想マシンによるオブジェクトの作成には、クラスロードチェック、オブジェクトへのメモリの割り当て、メモリ領域の初期化、オブジェクト設定、オブジェクトメソッドの実行の5つのステップがあります.
1クラスロードチェック
仮想マシンがnew命令に遭遇すると、まず、この命令のパラメータが定数プールでクラスのシンボル参照に位置決めできるかどうかを確認し、このシンボル参照が表すクラスがロード、解析、初期化されているかどうかを確認します.ない場合は、クラス・ロード・プロシージャを先に実行します.
2オブジェクトにメモリを割り当てる
オブジェクトに必要なメモリサイズは、クラスのロードが完了したときに決定されるため、オブジェクトにメモリを割り当てることは、Javaスタックでサイズが決定されたメモリ領域を分割することと同じです.
2つの割当て方法:
  • ポインタ衝突:Javaスタックのメモリは完全で、ポインタを空き空間に移動してオブジェクトのサイズと等しい距離
  • を移動します.
  • 空きリスト:Javaスタック内のメモリは完全ではありません.JVMは使用可能なメモリを記録するリストを維持し、割り当て時にリスト内の十分な空間をオブジェクトに分割し、リスト
  • を更新します.
    そのため、Javaスタックがどのように割り当てられるかは、ごみ収集器が圧縮整理機能を持っているかによって決まります.次のようになります.
  • Serial、ParNewなどはポインタ衝突
  • を採用
  • CMSは「タグ-クリア」に基づいて空きリスト
  • を採用する.
    スレッドセキュリティの問題
    同時性の場合、スレッドセキュリティの問題も考慮されます.2つのソリューション:
  • メモリ割り当て動作を同期処理する.JVMはCASに失敗を加えて再試行する方式で原子性
  • を保証する
  • メモリ割り当ての動作をスレッドごとに異なる空間に分割して行う.各スレッドは、Javaスタックに予め小さなメモリ(ローカルスレッド割り当てバッファTLAB)を割り当て、TLABが再割り当てを完了した場合にのみ同期ロックされます.

  • 3メモリ領域初期化
    JVMは、割り当てられたメモリ領域をゼロ値(オブジェクトヘッダを除く)に初期化します.TLABを使用すると、TLAB割当時に繰り上げて実行されます.
    このステップでは、オブジェクトインスタンスフィールドがJavaコードで初期値を付けずに直接使用できることを保証し、プログラムはこれらのフィールドのデータ型に対応するゼロ値にアクセスできます.
    4オブジェクトの設定
    JVMは、クラスメタデータ情報、オブジェクトのハッシュコード、オブジェクトのGC世代別年齢情報などのオブジェクトヘッダ情報を設定する.偏向ロックを有効にするかどうかもあります.
    5オブジェクトメソッドの実行
    このとき、JVMにとってオブジェクトは既に生成されている.Javaプログラムでは、メソッドを実行して初期化を開始したばかりで、オブジェクトが本当に作成されました.
    二、Javaオブジェクトの初期化
    Javaオブジェクトの初期化の過程では、主に3つのオブジェクトの初期化を実行する構造に関連し、それぞれインスタンス変数の初期化、インスタンスコードブロックの初期化、および構造関数の初期化である.
    インスタンス変数初期化とインスタンスコードブロック初期化
    インスタンス変数を定義(宣言)するとともに、インスタンス変数を直接割り当てるか、インスタンスコードブロックを使用して割り当てることもできます.この2つの方法でインスタンス変数を初期化すると、コンストラクション関数が実行される前に初期化操作が完了します.実際、インスタンス変数に直接値を割り当てるか、インスタンスコードブロックを使用して値を割り当てると、コンパイラはそのコードをクラスのコンストラクタに配置し、これらのコードはスーパークラスコンストラクタの呼び出し文の後(Super()に置かれ、関数自体のコードを構築する前に配置されます.
    public class Tdemo2 {
        //    
        private int i = 1;
        private int j = 1;
        public  Tdemo2(int c){
            System.out.println(i);
            System.out.println(j);
            this.i = c;
            System.out.println(i);
        }
        //   
        {
            j = j+1;
        }
        //     
        static{
            int a = 5;
            System.out.println(a);
        }
        public static void main(String[] args) {
            new Tdemo2(3);
        }
    }

    出力:
    5
    1
    2
    3

    実行順序はstaticコードブロック、メンバー変数付与、コードブロック、構造関数であることがわかります.
    コンストラクタの初期化
    Javaでは、作成したインスタンスの整合性を保証するために、クラスをインスタンス化する前に、そのスーパークラスをインスタンス化する必要があります.Javaでは、Objectオブジェクト(ObjectはJavaの最上位オブジェクトであり、スーパークラスはありません)以外のすべてのオブジェクトコンストラクション関数の最初の文は、スーパークラスコンストラクション関数の呼び出し文またはクラスで定義された他のコンストラクション関数でなければなりません.他のコンストラクション関数も明示的にスーパークラスのコンストラクション関数も呼び出していない場合は、コンパイラは、スーパークラス構造関数の呼び出しを自動的に生成します.
    実際には、クラスのオブジェクトをインスタンス化するプロセスは典型的な再帰プロセスです.
    クラスのオブジェクトをインスタンス化する準備をする前に、クラスの親をインスタンス化する準備をします.クラスの親に親がある場合は、クラスの親の親をインスタンス化し、Objectクラスに戻るまで順次再帰します.
    注:インスタンスの初期化は、クラスの初期化が終了してから初期化を開始する必要はありません.
    Javaでの付与順序を思い出す:1.親の静的変数付与2.自身の静的変数付与3.親メンバー変数付与と親コードブロック付与4.親構造関数付与5.自己メンバー変数付与と自己ブロックコード付与6.自己構造関数付与
    三、Javaオブジェクトの作成方法
    Javaオブジェクトの作成方法は5種類あります.
    1.newキーワード
    Person p = new Person();

    2.ClassクラスのnewInstance()(反射)
    Person p2 = Person.class.newInstance();

    3.ConstructorクラスのnewInstanceメソッド(反射)
    Constructor c = Person.class.getConstructor();
    Person p3 = (Person) c.newInstance();

    4.cloneメソッド(Cloneableインタフェースの実装)
    Person p4 = (Person) p3.clone();

    5.逆シーケンス化(Serializableインタフェースの実装)
    //   
    ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("person.txt"));
    output.writeObject(p);
    output.close();
    
    //   
    ObjectInputStream input = new ObjectInputStream(new FileInputStream("person.txt"));
    Person p5 = (Person) input.readObject();