1。基本概念
各Javaプログラムが実行されると、Javaプロセスが生成されます。各Javaプロセスには、1つ以上のスレッドが含まれる場合があります。各Javaプロセスは一意のJVMインスタンスに対応し、各JVMインスタンスはヒープに対応し、各スレッドには独自のプライベートスタックがあります。プロセスによって作成されたクラス(つまり、オブジェクト、つまり、オブジェクト)または配列(リファレンスではなく配列自体を参照)のすべてのインスタンスは、ヒープに配置され、プロセスのすべてのスレッドで共有されます。 Javaの割り当てられたヒープメモリは自動的に初期化されます。つまり、オブジェクトにメモリを割り当てるときに、オブジェクト内の変数が初期化されます。 Javaのすべてのオブジェクトのストレージスペースはヒープに割り当てられますが、このオブジェクトへの参照はスタックに割り当てられます。つまり、オブジェクトが作成されると、メモリがヒープとスタックに割り当てられます。ヒープに割り当てられたメモリは実際に作成されたオブジェクト自体を実際に保存しますが、スタックに割り当てられたメモリは、ヒープオブジェクトへの参照のみを保存します。ローカル変数の新品が出てくると、スペースがスタックスペースとヒープスペースに割り当てられます。ローカル変数のライフサイクルが終了すると、スタックスペースがすぐにリサイクルされ、ヒープスペース領域がGCがリサイクルするのを待っています。
特定の概念:JVMのメモリは、ヒープ、スタック、メソッド領域(方法、静的領域とも呼ばれます)の3つの領域に分けることができます。
スタックエリア:
1.保存されているのはすべてオブジェクトであり、各オブジェクトにはそれに対応する情報が含まれています(クラスの目的は操作指示を取得することです)。
2.JVMには1つのヒープ領域(ヒープ)のみがあり、すべてのスレッドで共有されています。ヒープは、基本的なタイプとオブジェクトの参照を保存するのではなく、オブジェクト自体と配列自体のみを保存します。
スタックエリア:
1.各スレッドには、基本的なデータ型自体とカスタムオブジェクトへの参照のみを保存するスタック領域が含まれています。
2。各スタックのデータ(プリミティブタイプとオブジェクト参照)はプライベートであり、他のスタックではアクセスできません。
3.スタックは3つの部分に分割されます。基本型変数領域、実行環境コンテキスト、および操作命令領域(操作手順の保存)。
メソッドエリア(静的領域):
1。すべてのスレッドで共有。メソッド領域にはすべてのクラスが含まれます(クラスはクラスの元のコードを指します。クラスオブジェクトを作成するには、クラスのコードをメソッド領域にロードして初期化する必要があります)と静的変数が含まれます。
2。メソッド領域には、クラスや静的変数など、プログラム全体で常に一意の要素が含まれています。
2。デモンストレーションの例
appmain.java
パブリッククラスAppmain //実行中、JVMはすべてのAppMainコードをメソッド領域に配置します{public static void main(string [] args)//メインメソッド自体はメソッド領域に配置されます。 {sample test1 = new Sample( "Test 1"); // test1は参照であるため、スタック領域に入れて、サンプルはカスタムオブジェクトであり、ヒープに配置する必要があります。サンプルテスト2 =新しいサンプル( "テスト2"); test1.printname(); test2.printname(); }} public class sample //実行中、jvmはAppmainのすべての情報をメソッド領域に入れます{ /**例名* /private文字列名; //新しいサンプルインスタンスの後、名前の参照がスタック領域に配置され、対応する文字列オブジェクトはヒープに配置されます/** constructor*/public sample(string name){this .name = name; } /** output* /public void printname()//オブジェクトがない場合、印刷メソッドはサンプルクラスとともにメソッド領域に配置されます。 {system.out.println(name); }}プログラムを実行するときは、最初にJava仮想マシンプロセスを開始します。このプロセスは、最初にClassPathからAppmain.classファイルを見つけ、ファイル内のバイナリデータを読み取り、次にランタイムデータ領域のメソッド領域にAppmainクラスのクラス情報を保存します。これは、AppMainクラスの読み込みプロセスです。
次に、Java仮想マシンは、メソッド領域のAppMainクラスのMain()メソッドのバイトコードを見つけ、その命令の実行を開始します。このmain()メソッドの最初のステートメントは次のとおりです。
コードコピーは次のとおりです。
サンプルtest1 = new Sample( "test1");
このステートメントの実行プロセス:
1. Java仮想マシンは、メソッド領域のサンプルクラスのタイプ情報を見つけましたが、サンプルクラスがメソッド領域にロードされていないため見つかりませんでした(Javaの内部クラスが別々に存在することがわかります。 Java仮想マシンはすぐにサンプルクラスをロードし、メソッド領域にサンプルクラスタイプ情報を保存します。
2. Java仮想マシンは、最初にヒープ領域の新しいサンプルインスタンスのメモリを割り当て、サンプルクラスタイプの情報がサンプルインスタンスのメモリに保存されるメソッド領域にメモリアドレスを保存します。
3。JVMプロセスでは、各スレッドには、スレッドの実行中に一連のメソッドコールプロセスを追跡するために使用されるメソッドコールスタックがあります。スタック内の各要素は、スタックフレームと呼ばれます。スレッドがメソッドを呼び出すたびに、新しいフレームがメソッドスタックに押し込まれます。ここのフレームは、操作中にメソッド、ローカル変数、および一時データのパラメーターを保存するために使用されます。
4. test1 "="は、main()メソッドで定義された変数(サンプルオブジェクトへの参照)であるため、main()メソッドを実行するメインスレッドのJavaメソッドコールスタックに追加されます。および「=」は、このtest1変数をヒープ領域のサンプルインスタンスに向けます。
5. JVMは、ヒープ領域に別のサンプルインスタンスを作成し続け、ヒープ領域で作成された新しいサンプルインスタンスを指すメインメソッドのメソッドコールスタックにtest2変数を追加します。
6。JVMは、printName()メソッドを順番に実行します。 Java仮想マシンがtest1.printname()メソッドを実行すると、Java仮想マシンは、ローカル変数Test1が保持している参照に基づいてヒープ領域のサンプルインスタンスを見つけ、次にサンプルインスタンスが保持している参照に基づいてサンプルクラスタイプの情報を見つけ、それによって、印刷物のbytecode()を取得します。
iii。区別する
Java言語のヒープとスタックの違い:
1.スタックとヒープは、両方ともJavaがRAMに保存するために使用される場所です。 C ++とは異なり、Javaはスタックとヒープを自動的に管理し、プログラマーはスタックやヒープを直接セットアップできません。
2。スタックの利点は、アクセス速度がヒープよりも速く、CPUに直接位置するレジスタに次ぐものであることです。しかし、欠点は、スタックのデータサイズと寿命が決定論的であり、柔軟性がないことです。さらに、スタックデータを共有できます(詳細については、以下の紹介を参照してください)。ヒープの利点は、メモリサイズを動的に割り当てることができ、寿命を事前にコンパイラに通知する必要がないことです。 JavaのGarbage Collectorは、使用されなくなったデータを自動的に収集します。しかし、不利な点は、メモリを実行時に動的に割り当てる必要があり、アクセス速度が遅くなることです。
Javaの2つのデータ型:
1つはプリミティブタイプで、8つのカテゴリ、すなわち、int、short、long、byte、float、double、boolean、charです(基本的なタイプの文字列はないことに注意してください)。このタイプの定義は、int a = 3などの形式で定義されます。長いb = 255l;自動変数と呼ばれます。自動変数には、クラスのインスタンスではなく、文字通りの値があります。つまり、クラスへの参照ではなく、ここにはクラスはありません。たとえば、int a = 3;ここでは、これらのリテラルデータのサイズと寿命のため、これらのリテラル値はプログラムブロックで定義され、プログラムブロックが終了した後にフィールド値が消え、速度を追求するためにフィールド値が消え、速度を追求するために存在するため、intタイプを指す基準を指す基準です。
スタックには非常に重要な機能があります。スタックに存在するデータを共有できます。同時に定義するとします:int a = 3; int b = 3;コンパイラはint a = 3を最初に処理します。最初に、スタック内の変数Aを持つ参照を作成し、3のリテラル値のアドレスがあるかどうかを確認します。1つが見つからない場合、リテラル値は3のアドレスを開き、3のアドレスを指します。 Bの参照変数を作成した後、スタックにはすでに3のリテラル値があるため、Bは3のアドレスを直接指します。このように、AとBは両方とも同時に3を指します。
リテラル値へのこの参照は、クラスオブジェクトの値とは異なります。 2つのクラスオブジェクトの参照が同時にオブジェクトを指していると仮定すると、1つのオブジェクト参照変数がオブジェクトの内部状態を変更した場合、他のオブジェクト参照変数はすぐにこの変更を反映します。代わりに、文字通りの参照を介してその価値を変更しても、それに応じて別の値が変更されません。上記の例のように、aとbの値を定義した後、a = 4とします。次に、bは4に等しく、3に等しくなりません。コンパイラ内で、a = 4が遭遇すると、スタックに4のリテラル値があるかどうかを再検索します。そうでない場合は、4の値を保存するためにアドレスを再開します。すでに存在する場合は、このアドレスを直接指してください。したがって、値aの変化は値bに影響しません。
別のタイプは、対応する基本データ型をラップする整数、文字列、ダブルなどのパッケージクラスデータです。これらのクラスデータはすべてヒープに存在します。 Javaは新しい()ステートメントを使用してコンパイラを表示し、実行時に必要に応じて動的にのみ作成するため、より柔軟になりますが、不利な点はより多くの時間がかかることです。
4。概要
Javaメモリ割り当て構造はまだ非常に明確です。徹底的に理解したい場合は、JVM関連の本を確認できます。 Javaでは、メモリ割り当てで最も厄介なことは文字列オブジェクトです。その特別な性質のため、多くのプログラマーは混乱を招く傾向があります。次の記事で詳しく説明します。