JAVAのメモリ割り当てと管理は、我々はJavaのメモリ管理およびJavaのメモリリークやゴミのリサイクルの知識を導入する前に、今日我々は再び、メモリの割り当てに関する詳細の深い、コアJava、Javaの知識ある、コアJava技術の一つであります。一般的なJavaのメモリに割り当ては、以下の分野が含まれます。
◆登録:我々は、プログラムで制御することはできません
◆スタック:基本データ型とオブジェクト参照を格納しますが、オブジェクト自体がスタック上に保存されていませんが、ヒープに格納されています
◆ヒープ:新しいによって生成されたデータの格納
◆静的フィールド:静的な定義に格納されているオブジェクトと静的メンバ
◆定数プール:定数の保管
◆非RAMストレージ:ハードディスク永続的ストレージスペース
Javaスタックメモリの割り当て
いくつかの参照変数とスタックメモリの機能が割り当てられ、基本的な機能で定義されたタイプの可変データオブジェクト。
あなたは、コードのブロック内の変数を定義すると、Javaの変数の出口スコープは、Javaが自動的に変数に割り当てられたメモリ空間に解放されますスタック変数のメモリ空間上で割り当てられたメモリ空間が瞬時にできますこれは、他の目的に使用されます。
Javaヒープメモリの割り当て
ヒープメモリは、新しいによって作成されたオブジェクトと配列を格納するために使用されます。ヒープに割り当てられたメモリでは、自動ガベージコレクタのJava仮想マシンによって管理されます。
ヒープは、配列またはオブジェクトを生成した後、この変数の値は、ヒープメモリのアレイまたはオブジェクトの先頭アドレスに等しくなるように、あなたはまた、スタック内のスタックを特別な変数を定義することができ、スタック変数となりました配列またはオブジェクト参照変数。あなたがプログラムにスタック参照変数を使用することができますので、参照変数は、配列やオブジェクトから名前と同じですヒープ配列やオブジェクトにアクセスします。参照変数は、名前の同等かからオブジェクトの配列です。
共通の基準変数が変数、スタック分布の定義で、リリース後にその範囲外の変数の実行を参照します。配列オブジェクトは、プログラムが新しい世代のコード・ブロック・アレイを使用することが進むも外側に、ヒープ自体に割り当てられたか、オブジェクトが配置され、オブジェクト自体によって占められるメモリのアレイが解除されず、それまでのオブジェクトの配列参照変数無しそれが無駄になったときに、それを使用することはできませんが、それでも不確実性のその後の期間は、ガベージコレクタ(解放)を奪われている中で、メモリ空間リンクを占めます。また、これは総メモリの理由Javaの比較です。
実際には、スタック変数は、Javaでのポインタである、メモリ変数をヒープを指すように!
定数プール(定数プール)
定数プールを参照するには、コンパイル時に決定され、ファイルコンパイルされた.classいくつかのデータを保存されます。:コード定義された(例えば、int型、長い、など)を含む基本的なタイプに加えて、および(例えば、文字列やアレイのような)オブジェクト・タイプは(最終)が一定値でさらにような参照テキストで発生するシンボルの数を含みます
◆完全修飾名のクラスとインタフェース。
◆名前と記述子フィールド。
◆名やメソッド名と記述子。
仮想マシンがロードされる各タイプの定数プールを維持する必要があります。定数プールは、タイプによって使用される定数のセットを注文し、直接定数(文字列、整数および浮動小数点定数)を含むとするれる他のタイプ、フィールドおよびメソッドのシンボルを参照します。
値が定数プールで文字列定数の場合。JVM定数プールテーブルメモリの形で存在しながら、テキスト文字列値メモを格納するための固定長CONSTANT_String_infoテーブルと文字列型について:テキストのみの文字列値が格納されていないこのテーブルはシンボル参照。ここでは、文字列定数プールの保管場所の値がより明確に理解している必要があります。
プログラムの実行中に、定数プールではなくヒープ、メソッド領域に格納されます。
ヒープとスタック
Javaヒープは実行時データ領域で、クラス(オブジェクト・スペースは、新しい確立することにより、これらのオブジェクトを分配、NEWARRAY、anewarray multianewarrayおよびその他の命令は、プログラムコードが、彼らは明示的に解放する必要はありません。ヒープガベージコレクションが担当しますヒープが動的に割り当てられたメモリサイズの利点である、生存率は、メモリの動的割り当て、Javaのガベージコレクタが自動的にそれはもはや実行時に使用されているこれらのデータを奪われているため、事前にコンパイラに指示する必要はありません。しかし、欠点は、実行時にメモリアクセス速度の遅い動的割当てによる、ということです。
登録した後、利点は、アクセス速度が原子炉よりも高速であるということであるスタック、スタックデータを共有することができます。しかし、欠点は、決定されなければならないスタックサイズと生存率のデータを、柔軟性の欠如があるということです。メインスタックストア可変データ(int型、短い、長い、バイト、フロート、ダブル、ブール、文字)のいくつかの基本的な種類、およびオブジェクトハンドル(参照)。
スタックは、非常に重要な特殊性は、スタック内のデータを共有することができるがあるということです持っています。我々はまた、定義するとします。
int型のA = 3; INT B = 3。
int型A = 3のコンパイラ最初の取引、見つからない、それは店舗3は、3点に来る場合は、最初、それは、スタック内の参照として変数を作成し、値3スタックがあるかどうかを確認します。治療INT B = 3続いて、Bがスタック内の値3を有するように、B 3を入れて、直接、参照変数を作成した後。したがって、aおよびbは同時に3を指す場合がありました。
この時点で、私たちが作る場合、= 4は、4値スタックがある場合、コンパイラは、再度検索します、と、その後4は、中保存されていない場合、および4にポイントを作る、あなたが既に持っている場合は、このアドレスを直接ポイントに。したがって、変更は、値bの値には影響を与えません。
この共有は、それは容易にそのような共有は、このような状況が変更Bに影響を与えないので、それはコンパイラによって行われる、異なるオブジェクトに2つの同時データ・オブジェクトを参照して注目すべきですスペースを節約。そして、オブジェクト参照変数は別のオブジェクト参照変数に影響します。このオブジェクトの内部状態を変更します。
文字列は特別なラッパークラスのデータです。あなたは使用することができます。
文字列str =新しいString( "ABC"); 文字列str = "ABC";
二つの形式を作成するには、最初のものである()新しいオブジェクトを作成するためには、新しいにヒープに格納されます。各呼び出しは、新しいオブジェクトを作成します。第二は、そうでない場合には、「ABC」が文字列定数内に格納されている、いかなる「ABC」が存在しない見つけるために、文字列定数プールへのシンボリック参照することにより、スタック上の参照変数STR文字列クラス・オブジェクトを作成し、そしてすることであるがプールは、とstrポイント「ABC」を作るために、あなたはすでに「ABC」を持っている場合はstrのポイント「ABC」の直接の原因です。
等号()メソッドを使用して、同一であるクラス値を比較するとき、==を使用して、同じオブジェクトへの2つのラッパークラスの基準点かどうかをテストする場合、上記の理論は、実施例で説明します。
文字列STR1 = "ABC"; 文字列STR2 = "ABC"; System.out.println(STR1 == STR2)。//真
同じオブジェクトへのSTR1とstr2ポイントで見ることができます。
文字列STR1 =新しい文字列( "ABC"); 文字列STR2 =新しい文字列( "ABC"); System.out.println(STR1 == STR2)。//偽
これは、異なるオブジェクトを生成するための新しい方法です。一度に各1。
だから、複数の「ABC」の文字列を作成するための第二の方法で、中に実際に、1つのオブジェクトのみがそれのメモリ内に存在している。有利なの文言とメモリ空間を節約すると同時に、それはJVMので、ある程度プログラムの動作速度を向上させることができます自動的に新しいオブジェクトを作成する必要があるかどうかを判断するためにスタックデータの実際の状況に応じました。文字列str =新しい文字列(「ABCのために 」); コード、平坦それによってプログラムの負担を増加させる、新しいオブジェクトを作成する必要があるか否かにかかわらず、その文字列値の、等しく、ヒープ内の新しいオブジェクトを作成します。
一方、注意を払う:私たちは、このような文字列STR =「ABC」としての使用、フォーマット定義されたクラスの時間、いつも当たり前のオブジェクトのstr Stringクラスを作成します。心配罠!オブジェクトが作成されないことがあります!しかし、あなたはただ、以前に作成したオブジェクトへのポイントを有することができます。唯一の新しい()メソッドを介して各時間が新しいオブジェクトが作成されていることを確認します。
Stringクラスの不変の性質は、String型の変数は常にその値を変更する際に必要なので、プログラムの効率を向上させるために、StringBufferクラスを使用して検討すべきです。
1.最初の文字列の基本データ型のない8種類は、文字列オブジェクトです。オブジェクトのデフォルト値はnullであるため、文字列のデフォルト値はnullであるので、それは特別なオブジェクトである、機能のいくつかの他のオブジェクトがありません。
2.新しいString()と新しい文字列(「」)が新しい、空の文字列、空の文字列がnullではないと宣言されます。
3.文字列str = "kvill";の差分文字列str =新しい文字列( "kvill")
例:
文字列S0 = "kvill"。 文字列S1 = "kvill"。 文字列s2を= "KV" + "病気"。 System.out.println(S0 == S1)。 System.out.println(S0 == S2)。
結果は次のとおりです。
trueに
真に
まず第一に、私たちは道のために結果を知りたいJAVAは、文字列定数のコピーを1つだけ確実にします。
ので「kvill」文字列定数は、それらがコンパイル時に決定されているS0とS1の例は、あるS0、S1 ==真のように、そして「KV」と「病気」もある文字列定数、接続が一定の文字列に解析され、定数文字列定数と確かに、独自の、そのまた、S2のコンパイルで文字列を複数によってなされる文字列は、定数プールはのS2「kvillを」であります参照。だから我々は、S0 == S1 == S2になってきた。文字列新しいString(よう)(新しいString文字列を使用して作成され一定ではなく、それは、コンパイル時に決定することはできません)定数プールに入れないで作成するために、彼らは自分を持っていますアドレス空間。
例:
文字列S0 = "kvill"。 文字列S1 =新しい文字列( "kvill"); 文字列s2を= "KV" +新しいString( "病気"); System.out.println(S0 == S1)。 System.out.println(S0 == S2)。 System.out.println(S1 == S2)。
結果は次のとおりです。
偽
偽
偽
例2 S0新しいオブジェクトまたは定数プール「kvill」アプリケーション、彼らはコンパイル時に決定することはできませんのでs1が、そのために新しい文字列の後半部分の「kvill」の参照、S2ランタイムの創造である(「病気」)ので、コンパイル時に決定することができない、それはまた、新しいオブジェクト「kvill」アプリケーションの作成であり、これらは、なぜこのような結果を知って来る理解すること。
4. String.intern():
少し紹介追加するには、次の.classファイルは定数プール内に存在し、それが実行時のJVMにロードされ、かつ拡張することができます。コールインターン()メソッドは、Java(登録商標)の参照が返され、同じUnicode文字列定数定数プールは、もしあれば、存在するか否かを見つけたとき場合、STR Stringインスタンス、文字列のインターン()メソッドは、定数プールを拡大するための方法でありますいいえ、増加は、Unicode文字列strに等しく、定数プール内のそれへの参照を返す;参照例が明らかであろう
例:
文字列S0 = "kvill"。 文字列S1 =新しい文字列( "kvill"); 文字列s2 =新しい文字列( "kvill"); System.out.println(S0 == S1)。 System.out.println( "**********"); s1.intern(); S2 = s2.intern()。//把常量池中"kvill"的引用赋给S2 のSystem.out.println(S0 == S1)。 System.out.println(S0 == s1.intern())。 System.out.println(S0 == S2)。
:結果は
偽
実装s1.internにもかかわらず、偽//()が、その戻り値がS1に割り当てられていない
真//説明s1.internを()「kvill」への参照定数プールを返し
、真
最後に、私は間違って理解を取り除くでしょう:一部の人が言う、「あなたはグローバルテーブルにStringクラスの文字列を保存することができます使用String.intern()メソッド、もしすでにテーブルに同じ値を持つUnicode文字列、テーブルの文字列でなく、同じ値が、その後、自分のアドレスがテーブルに登録されている場合、この方法は、すでに文字列テーブルのアドレスを返します。「私はこのグローバル文字列定数プールテーブルがケースであると理解置けば、彼は言いました、 「文字列は、テーブル内の同じ値でない場合は、テーブルに登録され、そのアドレスが」彼の最後の言葉は、間違っていました。
例:
文字列S1 =新しい文字列( "kvill"); ストリングS2 = s1.intern()。 System.out.println(S1 == s1.intern())。 System.out.println(S1 +」「+ S2)。 System.out.println(S2 == s1.intern())。
結果:
偽
kvillのkvill
trueに
このクラスでは、我々は評判を持っていない「kvill」定数、我々は(s1.intern呼び出すときに一定のプール「kvill」の始まりは、定数プールは、オリジナルの新しい「kvill」定数を追加した後)ではありません定数プール「kvill」はまだ存在しないので、それは「定数プールに自身のアドレスを登録して」だったではありません。
S1 == s1.intern()がfalseの説明オリジナルの「kvill」はまだ存在であり、S2、今「kvill」定数プールのアドレスなので、そこS2 == s1.intern()が真です。
equalsと5.()と==:
この2つの文字列ではなく、それらが等しい場合はtrueを返すかどうかの文字列Unicodeの配列比較のために、単純であり、==アドレスは、それはそれは引用符で囲まれた文字列であるかどうか、2つの文字列が同じで比較されています。
文字列について6.不変です
これは、たくさんのような、限り、あなたは一度作成された文字列のインスタンスは変更されないことを知っているように、と言っていると言う:文字列STR =「KV」+「+「病気」」+「ANS」;つまり、4つの文字列があります定数は、すべて「KV」と、メモリ内の「病気」生成「kvill」、との最初の「kvillは」再び、および「」メモリに格納されている「kvill」を生成し、最後に「kvill ANS」を生成し、この文字列をSTRに割り当てられたアドレス、理由の文字列「不変」のStringBufferの使用が提案理由のStringBufferは変更可能であるため、ある、一時的な変数の多くを持っていました。
ここでは文字列に関連するいくつかの一般的な質問は以下のとおりです。
最後の文字列の使用状況や理解の
= StringBufferのStringBufferの新しい新ファイナル( "111"); 最終のStringBuffer StringBufferの新しい新B =( "222"); A = B; //ないコンパイラ文によって 最終のStringBuffer StringBufferの新しい新しい= A( "111"); a.append ( "222"); //コンパイル
「値」に見え、最終的に参照のみ(すなわち、メモリアドレス)が有効である、それはオブジェクト参照が唯一の変更点の最初のポイントへのポイントは、コンパイル時エラーが発生しますことができます強制します。それは、オブジェクトの変更にポイントとして、最終的には責任を負いません。
いくつかの例の文字列定数プールの問題
ここで比較分析や理解のいくつかの一般的な例は以下のとおりです。
列A = "A1"。 列B = "" + 1。 System.out.println((==のb)参照)。//結果=真 列A = "ATRUE"。 列B = "" + "真"; System.out.println((==のb)参照)。//結果=真 列A = "A3.4"。 列B = "" + 3.4。 System.out.println((==のb)参照)。//結果=真
分析:コンパイルの「+」接続に「+」に接続され、最適化、JVMは、定数文字列値接続後、通過「」+ 1、コンパイラの最適化を取るだろうための一定のJVM文字列クラスの後にA1にすでにでした。上記の手順の最終結果が真であるようにコンパイル時に値は、文字列定数として設定されます。
列A = "AB"。 文字列BB = "B"; 列B = "" + BB。 System.out.println((==のb)参照)。//結果=偽
分析:参照文字列についてJVMは、「+」接続のために文字列は、参照文字列が存在し、参照することによりコンパイルされたプログラムの値を決定することができない、すなわち、「」+ BBコンパイラを最適化することができません、唯一の動的な接続後に新しいアドレスの割り当てとプログラムの実行中にBを割り当てます。したがって、上記の手順の結果も偽です。
列A = "AB"。 最終列BB =「B」。 列B = "" + BB。 System.out.println((==のb)参照)。//結果=真
分析:[3]の唯一の違いは、それらの定数プールへのコンパイル時定数の値にローカルメモリのコピーとして解釈以上に埋め込まれている変数の最終的な変更のための最終的な変形BBストリングの添加であり、そのバイトコードストリーム。したがって、この場合には「」+ BBと「」+「B」の効果は同じです。したがって、上記の手順の結果は真です。
列A = "AB"。 最終的な文字列のBB = getBB()。 列B = "" + BB。 System.out.println((==のb)参照)。//結果=偽 プライベート静的な文字列getBB(){ リターン"B"; }
分析:文字列参照BB用のJVMは、その値がコンパイル時にのみ、プログラムを実行するメソッドを呼び出した後に決定することができない、この方法は、「」に接続され、動的に割り当てられたアドレスをbの値を返すように上記の手順の結果それは偽です。
すなわち、上記4つの例により得ることができます。
文字列s = "" + "B" + "C";
就等价于文字列s = "ABC"。
列A = "A";
列B = "B";
文字列C = "C";
文字列s = A + B + C。
これが当てはまらない場合、最終的な結果は、に等しいです。
StringBufferのの一時=新しいStringBufferを(); temp.append(A).append(B).append(C); 文字列s = temp.toString()。
上記解析結果により、文字列オペレータに参加することができることを推測することは容易である(+)分析は非効率的に、このコードのような形。
パブリッククラスTest { パブリック静的な無効メイン(文字列引数[]){ 文字列S = NULL; (I 0 = int型、iが100 <; I ++の)ため、{ S + = "A"。 } } }
それぞれ、+ StringBuilderオブジェクトを生成し、その後、投げた後、追加を行います。次のサイクルに到達する前にStringBuilderオブジェクトを再生し、その後最後までように文字列を追加し、そして。私たちは、直接オブジェクトのStringBuilderアペンドを採用した場合、我々はNを保存することができます - 1回を作成し、時間のオブジェクトを破壊します。したがって、文字列を適用するためのサイクルに接続する、または一般的に動作を追加するためのStringBuffer StringBuliderオブジェクトを使用しています。
理解し、分析するStringオブジェクトのインターン方法:
パブリッククラスTEST4 { プライベート静的列A =「AB」。 パブリック静的無効メイン(文字列[] args){ ストリングS1 = "A"。 文字列s2 = "B"; ストリングS = S1 + S2。 System.out.println(S == A); //偽 するSystem.out.println(s.intern()== A); //真 } }
ここで使用する定数プールの問題であるJAVAを。S1 + S2の動作のために、実際に新しいオブジェクトを再作成するために、反応器の内部に、sはヒープ領域の新たなターゲットコンテンツに格納されている、およびsの値は等しくありません。()メソッドが、しかし場合、定数プールのように、それは、定数プールのコールs.internのアドレス値を返すことがあり、それが記憶されているs.intern値に等しい値です。
概要
元のデータとデータタイプのオブジェクト参照(文字列、アレイオブジェクト、等)の一部を格納するために使用されるローカル変数のスタックが、オブジェクトの内容を記憶していません
ヒープは、新しいキーワードを使用して作成されたオブジェクトを格納します。
文字列は特別なラッパークラスであり、その参照がスタックに格納され、固定(定数プールとヒープ)を作成する様々な方法で対象コンテンツに基づいていなければならない。いくつかは、文字列定数プール内に作成し、保存されたコンパイル、およびいくつかは、ヒープ上に格納された新しいキーを使用して実行されているときに作成されます。
転送:https://www.oschina.net/question/12_11121(深いコアJavaのJavaのメモリ割り当ての原則簡潔)