JavaのヒープとスタックJavaは、メモリを2つのタイプに分割します。1つはスタックメモリ、もう1つはヒープメモリです。
関数で定義されたいくつかの基本的なタイプの変数とオブジェクトの参照変数は、関数のスタックメモリに割り当てられます。変数がコードのブロックで定義されている場合、Javaはスタック内の変数のメモリスペースを割り当てます。変数の範囲が変数の範囲を超えると、Javaは変数に割り当てられたメモリ空間を自動的にリリースし、メモリ空間はすぐに個別に使用できます。
ヒープメモリは、新しいによって作成されたオブジェクトと配列を保存するために使用されます。ヒープに割り当てられたメモリは、Java Virtual Machine Automatic Garbage Collectorによって管理されます。ヒープで配列またはオブジェクトが生成された後、スタックで特別な変数を定義できます。この変数の値は、ヒープメモリ内の配列またはオブジェクトの最初のアドレスに等しくなります。スタック内のこの特別な変数は、配列またはオブジェクトの参照変数になります。将来的には、プログラムのスタックメモリ内の参照変数を使用して、ヒープ内の配列またはオブジェクトにアクセスできます。参照変数は、配列またはオブジェクトのエイリアスまたはコード名に相当します。
参照変数は通常の変数です。定義すると、メモリはスタックに割り当てられます。参照変数は、プログラムが実行されるときにスコープの外側にリリースされます。アレイとオブジェクト自体はヒープに割り当てられます。プログラムが、配列とオブジェクトを生成するために新しいステートメントを使用しているコードブロックの外側で実行されたとしても、配列とオブジェクト自体が占めるヒープメモリはリリースされません。配列とオブジェクトは、それを指す参照変数がない場合にのみゴミになり、使用できなくなりますが、まだメモリを占有し、不確実な時間にゴミコレクターによって解放されます。これが、Javaがより多くの記憶を取り上げる主な理由でもあります。実際、スタックの変数は、Javaのポインターであるヒープメモリの変数をポイントします!
Javaのヒープとスタック
Javaはメモリを2つのタイプに分割します。1つはスタックメモリ、もう1つはヒープメモリです。
1.スタックとヒープは、両方ともJavaがRAMに保存するために使用される場所です。 C ++とは異なり、Javaはスタックとヒープを自動的に管理し、プログラマーはスタックやヒープを直接セットアップできません。
2。スタックの利点は、アクセス速度がヒープよりも速く、CPUに直接位置するレジスタに次ぐものであることです。しかし、欠点は、スタックのデータサイズと寿命が決定論的であり、柔軟性がないことです。さらに、スタックデータを共有できます。ヒープの利点は、メモリサイズを動的に割り当てることができ、寿命を事前にコンパイラに通知する必要がないことです。 JavaのGarbage Collectorは、使用されなくなったデータを自動的に収集します。しかし、不利な点は、メモリを実行時に動的に割り当てる必要があり、アクセス速度が遅くなることです。
3. Javaには2種類のデータがあります。
1つは基本的なタイプ(プリミティブタイプ)です。つまり、int、short、long、byte、float、double、boolean、char(注)、8つのタイプがあります(注意してください。
文字列の基本的なタイプはありません)。このタイプの定義は、int a = 3などの形式で定義されます。長いb = 255l;自動変数と呼ばれます。自動変数には、クラスのインスタンスではなく、クラスへの参照ではなく、ここにはクラスがないことは文字通りの値が含まれていることに注意してください。たとえば、int a = 3;ここでは、intタイプを指す参照です、
3のリテラル値を指します。これらのリテラル値のサイズにより、これらのリテラル値の寿命は既知です(これらのリテラル値はプログラムブロックで正当に定義され、プログラムブロックが終了した後、フィールド値は消えます)。
速度を追求するために、それはスタックに存在します。
さらに、スタックの非常に重要な特別な機能は、スタック内のデータを共有できることです。同時に定義するとします。
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は新しい()ステートメントを使用してコンパイラを表示し、実行時に必要に応じて動的にのみ作成するため、より柔軟になりますが、不利な点はより多くの時間がかかることです。
Javaには、データを保存できる6つの異なる場所があります。
1.登録。これは、他のストレージエリア(プロセッサ)とは異なる場所にあるため、最速の保管エリアです。ただし、レジスタの数は非常に限られているため、レジスタは要件に応じてコンパイラによって割り当てられます。直接制御することも、プログラムに登録簿の存在の兆候を感じることもできません。
2。スタック。一般的な目的のRAMにありますが、「スタックポインター」を備えたプロセッサからサポートを得ることができます。スタックポインターが下に移動すると、新しいメモリが割り当てられます。それが上昇すると、それらの記憶は解放されます。これは、レジスタに2番目のストレージを割り当てるための高速で効率的な方法です。プログラムを作成するとき、Javaコンパイラは、スタックに保存されているすべてのデータの正確なサイズとライフサイクルを知る必要があります。これは、スタックポインターを上下に移動するために対応するコードを生成する必要があるためです。この制約により、プログラムの柔軟性が制限されるため、一部のJA VAデータはスタックに保存されますが、特にオブジェクトの参照、Javaオブジェクトは保存されません。
3。ヒープ。いわゆるJavaオブジェクトを保存するためのユニバーサルメモリプール(RAMにも存在します)。ヒープの利点は、コンパイラがヒープから割り当てるためのストレージエリアの数を知る必要がないこと、また、保存されたデータがヒープでどれだけ長く続くかを知る必要がないことです。したがって、ヒープにストレージを割り当てることには大きな柔軟性があります。オブジェクトを作成する必要がある場合、新しいコードの単純な行を書くだけです。このコード行を実行すると、ヒープに自動的に保存および割り当てられます。もちろん、この柔軟性のために対応するコードを支払う必要があります。スタックで保存するよりも、ヒープでストレージを割り当てるには時間がかかります。
4。静的ストレージ。ここで「静的」は「固定位置で」を意味します。静的ストレージは、プログラムが実行されているときに常に存在していたデータを保存します。キーワード静的を使用して、オブジェクトの特定の要素が静的であることを識別できますが、Javaオブジェクト自体は静的ストレージスペースに格納されることはありません。
5。定数ストレージ。通常、一定の値はプログラムコード内に直接保存され、変更されないため、安全です。埋め込まれたシステムでは、定数自体が他の部分から分離される場合があるため、この場合、ROMに配置することはオプションです。
6。非RAMストレージ。データがプログラムの外で完全に生き残っている場合、プログラムを制御せずに残すことができ、プログラムが実行されていないときに存在する可能性があります。
速度に関しては、次のように関係があります。
登録<stack <heap <other
「上記の通路は「Javaの思考」から抽出されています」
質問1:
string str1 = "abc"; string str2 = "abc"; System.out.println(str1 == str2); //真実
質問2:
string str1 = new String( "ABC"); string str2 = new String( "ABC"); System.out.println(str1 == str2); // 間違い
質問3:
文字列s1 = "ja";文字列s2 = "va";文字列s3 = "java";文字列S4 = S1 + S2; System.out.println(s3 == s4); // false system.out.println(s3.equals(s4)); // true
オブジェクトの関数と参照変数で定義されたいくつかの基本的なタイプの変数はすべて、関数のスタックメモリに割り当てられます。
変数がコードのブロックで定義されている場合、Javaはスタック内のこの変数のメモリスペースを割り当てます。変数の範囲が変数を超えると、Javaは変数に割り当てられたメモリスペースを自動的にリリースし、メモリスペースはすぐに個別に使用できます。
ヒープメモリは、新しいによって作成されたオブジェクトと配列を保存するために使用されます。
ヒープに割り当てられたメモリは、Java Virtual Machineの自動ゴミコレクターによって管理されます。
アレイまたはオブジェクトがヒープで生成された後、スタック内のこの変数の値がヒープメモリ内のアレイまたはオブジェクトの最初のアドレスに等しくなり、スタック内の変数がアレイまたはオブジェクトの参照変数になります。
参照変数は、配列またはオブジェクトに与えられた名前と同等です。プログラムのスタック内の参照変数を使用して、ヒープ内の配列またはオブジェクトにアクセスできます。
具体的には、スタックとヒープはどちらもJavaがRAMに保存するために使用する場所です。 C ++とは異なり、Javaはスタックとヒープを自動的に管理し、プログラマーはスタックやヒープを直接セットアップできません。
Java Heapはランタイムデータ領域であり、そこからオブジェクトがスペースを割り当てます。これらのオブジェクトは、New、NewArray、Anewarray、MultianWarrayなどの指示を通じて確立されます。プログラムコードを明示的にリリースする必要はありません。ヒープはゴミ収集を担当します。ヒープの利点は、メモリサイズを動的に割り当てることができることであり、ランタイムでメモリを動的に割り当てるため、寿命をコンパイラに事前に通知する必要がないことです。 JavaのGarbage Collectorは、使用されなくなったデータを自動的に収集します。しかし、欠点は、実行時にメモリを動的に割り当てる必要があるため、アクセス速度が遅くなることです。
スタックの利点は、アクセス速度がヒープよりも高速で、レジスタに次ぐものであり、スタックデータを共有できることです。しかし、欠点は、スタックのデータサイズと寿命が決定論的であり、柔軟性がないことです。スタックは、主にいくつかの基本的なタイプの変数(、int、short、long、byte、float、double、boolean、char)とオブジェクトハンドルを保存します。
スタックの非常に重要な特別な機能は、スタックに存在するデータを共有できることです。同時に定義するとします。
int a = 3;
int b = 3;
コンパイラはint a = 3を最初に処理します。最初に、変数Aを持つスタックに参照を作成し、次にスタックに3の値があるかどうかを確認します。発見されていない場合は、3を保存してから3を指します。 bの参照変数を作成した後、スタックにはすでに3の値があるため、Bは直接3に指されます。このように、AとBは両方とも同時に3を指します。この時点で、a = 4が再び設定されている場合。その後、コンパイラは、スタックに4値があるかどうかを再度検索します。そうでない場合は、4を保存し、Aを4をポイントします。すでに存在する場合は、このアドレスを直接指してください。したがって、値aの変化は値bに影響しません。このデータの共有は、同時に1つのオブジェクトを指す2つのオブジェクトからの参照の共有とは異なることに注意してください。この場合、Aの変更はBに影響しないため、スペースを節約するのに役立つコンパイラによって行われます。オブジェクト参照変数は、このオブジェクトの内部状態を変更し、別のオブジェクト参照変数に影響します。
文字列は特別なパッケージデータです。使用できます:
string str = new String( "ABC"); string str = "abc";
作成する2つのフォームがあります。 1つ目は、new()を使用して新しいオブジェクトを作成することです。これはヒープに保存されます。新しいオブジェクトが呼び出されるたびに作成されます。
2番目のタイプは、最初にスタック内の文字列クラスのオブジェクトに可変STRを作成し、次にスタックに「ABC」が保存されているかどうかを確認することです。そうでない場合は、「ABC」をスタックに保存し、STRを「ABC」を指します。すでに「ABC」がある場合は、STRを「ABC」を直接指してください。
クラスの値が等しいかどうかを比較する場合、equals()メソッドを使用します。 2つのラッパークラスの参照が同じオブジェクトを指しているかどうかをテストするときは、==を使用し、以下の例を使用して上記の理論を説明します。
string str1 = "abc"; string str2 = "abc"; System.out.println(str1 == str2); //真実
Str1とStr2が同じオブジェクトを指していることがわかります。
string str1 = new String( "ABC"); string str2 = new String( "ABC"); System.out.println(str1 == str2); // 間違い
新しい方法は、異なるオブジェクトを生成することです。一度に1つずつ生成します。
したがって、2番目の方法では、複数の「ABC」文字列が作成され、メモリには1つのオブジェクトのみがあります。この執筆方法は有益であり、メモリスペースを保存します。同時に、JVMはスタック内のデータの実際の状況に基づいて新しいオブジェクトを作成する必要があるかどうかを自動的に決定するため、プログラムの実行速度をある程度改善できます。 string str = new String( "ABC");のコードの場合、新しいオブジェクトが等しいかどうかに関係なく、新しいオブジェクトが新しいオブジェクトを作成する必要があるかどうかに関係なく作成され、それによりプログラムの負担が増加します。
一方、注:文字列str = "abc"などの形式を使用してクラスを定義する場合、文字列クラスのオブジェクトSTRを作成することを常に当たり前のことと考えています。 (必ずしもそうではありませんが、事前に作成されるため、それは作成されます。これはオブジェクトの作成です。すでにある場合は、元のオブジェクトを指してください)!オブジェクトは作成されていない可能性があります!そして、以前に作成されたオブジェクトを指すだけかもしれません。新しい()メソッドを介してのみ、毎回新しいオブジェクトが作成されるようにすることができます。文字列クラスの不変の性質により、文字列変数が頻繁にその値を変換する必要がある場合、StringBufferクラスを使用してプログラムの効率を向上させることを検討する必要があります。