文字列a = "123"、string b = new string( "123")など、インターネット上の主要なセクションで実行するときに、Java文字列のメモリ割り当てに関する議論をよく見ることができます。実際、これら2つの形式の文字列「123」の文字通りの値は、スタックにも、実行時にヒープに保存されません。メソッド領域の特定の一定の領域に保存され、同じ文字列リテラル値のメモリに1つのコピーのみが保持されます。以下の例で分析します。
1。==演算子は、2つの文字列参照比較が比較される2つの場合に使用されます。
public class stringtest {public static void main(string [] args){// part 1 string s1 = "i love china";文字列S2 = "私は中国が大好きです"; system.out.println( "result:" + s1 == s2); //プログラムの実行結果はtrue //パート2文字列s3 = new String( "I Love China");文字列S4 = new String( "I Love China"); System.out.println( "result:" + s3 == s4); //プログラムの実行結果はfalse}}}Javaの==演算子は変数の値を比較していることがわかります。参照型に対応する変数の値は、参照オブジェクトのアドレスを保存します。ここでは、文字列は参照タイプであり、ここの4つの変数の値は実際に文字列のアドレスを保存します。 PART2の実行結果は明らかです。なぜなら、新しい演算子により、JVMが実行時にヒープに新しいオブジェクトを作成するため、2つの異なるオブジェクトのアドレスが異なるためです。ただし、PART1の実行結果から、S1とS2が指摘されているアドレスと同じであることがわかります。それでは、変数S1とS2が保存した文字列はどこに向けられていますか? JVMは文字列をどのように処理しますか?同様に、変数S3およびS4で指摘されたヒープ内のさまざまな文字列オブジェクトについて、彼らは独自のオブジェクト空間に「I Love China」文字列を保存しますか? JVMが文字列を処理する方法を理解するために、最初にJavaコンパイラによって生成されたバイトコード命令を調べます。 ByteCode命令により、JVMが実行する操作を分析します。
2.以下は、プログラムによって生成されたいくつかのバイトコード情報です。赤は私たちが注意を払う必要がある部分をマークします。
定数プール:#1 =クラス#2 // StringTest#2 = UTF8 STRINGTEST#3 =クラス#4 // Java/Lang/Object#4 = UTF8 Java/Lang/Object#5 = UTF8 <Init>#6 = UTF8()V#7 = UTF8コード#8 = MethodRef#3。 #5:#6 // "<init>" :()v#10 = utf8 linenumbertable#11 = utf8 localvariabletable#12 = utf8 this#13 = utf8 lstringtest; #14 = UTF8 MAIN#15 = UTF8([LJAVA/LANG/STRING;)V#16 = STRING#17 // LOVE CHINA LIVE LIVE STRING ADDRESS#17 = UTF8 I LOVE CHINA#18 = FIELDREF#19。#21 // java/lang/system. #19 =クラス#20 // java/lang/system#20 = utf8 java/lang/system#21 = nameandtype#22:#23 // out:ljava/io/printstream; #22 = utf8 out#23 = utf8 ljava/io/printstream; #24 =クラス#25 // java/lang/stringbuilder#25 = utf8 java/lang/stringbuilder#26 = string#27 // result:#27 = utf8 result#24。#29 // java/lang/stringbuilder。 "<init; // "<init>" :( ljava/lang/string;)v#30 = utf8(ljava/lang/string;)v#31 = methodref#24。#32 // java/lang/stringbuilder.append:(z)ljava/lang/stringbuilder;追加:(z)ljava/lang/stringbuilder;#33 = utf8付録#34 = utf8(z)ljava/lang/stringbuilder;#35 = methodref#24。#36 // java/lang/stringbuilder.tostring :()ljava/lang/lang/string; tostring :()ljava/lang/string;#37 = utf8 tostring#38 = utf8()ljava/lang/string;#39 = methodref#40。#42 // java/io/printstream.println:(ljava/lang/string; Java/io/printstream#42 = nameandtype#43:#30 // println :( ljava/lang/string;)v#43 = utf8 println#45 // java/lang/string#45 = utf8 java/lang/string java/lang/string。 "<init>" :( ljava/lang/string;)v#47 = utf8 args#48 = utf8 [ljava/lang/string;#49 = utf8 s1#50 = utf8 ljava/lang/string;#51 = utf8 s2 stackmaptable#55 = class#48 // "[ljava/lang/string;"#56 = utf8 sourcefile#57 = utf8 stringtest.java ............ //対応するメソッドのバイトコード命令は、JVMランタイムによって説明および実行されます。 public static void main(java.lang.string []);記述子:([ljava/lang/string;)v flags:acc_public、acc_static code:stack = 4、stack = 4、locals = 5、args_size = 1 0:ldc#16 //文字列私は中国が大好きです。この命令は、次の命令2に対応します。S1=「I Love China」ステートメント2プログラムのステートメント2:Store_1 //スタックの上部にオブジェクト参照をローカル変数に割り当てます。この命令と次の命令5は、プログラムの文字列S2 = "I Love China"ステートメントに対応しています。 5:store_2 //スタックの上部にあるローカル変数へのオブジェクトの参照を割り当てる2。6:getstatic#18 // field java/lang/system.out:ljava/io/printstream; 9:新しい#24 //クラスJava/Lang/StringBuilder 12:DUP 13:LDC#26 //文字列結果:15:Invokespecial#28 // Method Java/Lang/StringBuilder。」等しいかどうかを比較するためにスタック27に移動し、次の命令を実行、実行して実行し、等しく実行します23:iconst_1 24:goto 28 27:iconst_0 28:invokevirtual#31 // method java/lang/stringbuilder.Append:(z)ljava/lang/stringbuilder; 31:InvokeVirtual#35 // Method Java/Lang/StringBuilder.ToString :()Ljava/Lang/String; 34:InvokeVirtual#39 //方法Java/io/printstream.println :( ljava/lang/string;)v 37:new#44 // class java/lang/string、オブジェクトを作成します。 40:dup //スタックの上部にオブジェクトのコピーをコピーして、スタックの上部に押します。 41:LDC#16 //文字列私は中国が大好きで、0、3の指示と同じです。 43:Invokespecial#46 // Method Java/Lang/String。 "<init>" :( ljava/lang/string;)v 46:store_3 47:new#44 // class java/lang/string //オブジェクトを作成し、オブジェクトをスタック50:dup 51:ldc#16/string i string of to the sting of to the string of the strick 53:Invokespecial#46 // Method Java/Lang/String。 "<init>" :( ljava/lang/string;)v、スタックの上部にある対応するオブジェクト参照に従ってオブジェクトの初期化方法を呼び出し、文字列オブジェクト56:ストア4 //オブジェクト4:getStatic#18///////// java/lang/system.out:ljava/io/printstream; 61:新しい#24 //クラスJava/Lang/StringBuilder 64:DUP 65:LDC#26 //文字列結果:67:Invokespecial#28 // Method Java/Lang/StringBuilder.Append:(Z)Ljava/Lang/StringBuilder; 84:InvokeVirtual#35 // Method Java/Lang/StringBuilder.ToString :()Ljava/Lang/StringBuilder.Append:(Z)Ljava/Lang/StringBuilder; 84:InvokeVirtual#35 // Method Java/Lang/StringBuilder.ToString :()Ljava/Lang/StringBuilder; 87:InvokeVirtual#39 //方法Java/io/printstream.println :( ljava/lang/string;)v 90:return ......... linenumbertable:line 7:0line 8:3line 9:6line 11:37line 12:47line 13:58line 14:90localiaiabretable argutable [ljava/lang/string; //ローカル変数088 1 S1 ljava/lang/string; //ローカル変数185 2 S2 ljava/lang/string; //ローカル変数244 3 s3 ljava/lang/string; //ローカル変数333 4 S4 ljava/lang/string; //ローカル変数4
Bytecodeの赤い部分は、私たちの議論に関連しています。生成されたバイトコードを介して、例プログラムに次の結論を描画できます。
1)。 JavaコンパイラがプログラムをBytecodeにコンパイルすると、文字列定数「I Love China」が最初にBytecode定数プールに存在するかどうかを決定します。存在しない場合、作成しません。つまり、等しい文字列が予約されています。シンボリック参照を介して1つのコピーのみを見つけることができるため、プログラムの文字列変数S1とS2は、定数プールの同じ文字列定数を指します。実行時に、JVMは、一般に定数プールと呼ばれるメソッド領域の位置にバイトコード定数プールに文字列定数を保存し、文字列は文字配列の形でインデックスを介してアクセスされます。 JVMは、実行時にS1とS2を指す文字列の相対的な参照アドレスを、S1とS2を指している文字列の実際のメモリアドレスに指します。
2)。文字列S3 = new String( "I Love China")、String S4 = new String( "I love china")の場合、bytecodeから、新しい命令を呼び出すことがわかります。 JVMは実行時に2つの異なるオブジェクトを作成し、S3とS4は異なるオブジェクトアドレスを指します。したがって、S3 == S4の比較の結果は偽です。
第二に、S3およびS4オブジェクトの初期化の場合、バイトコードからオブジェクトのinitメソッドが呼び出され、一定のプールの参照「I Love China」が渡されます。それでは、文字列オブジェクトの作成と初期化は何ですか?文字列のソースコードと文字列オブジェクトによって生成されたバイトコードを確認して、文字列がオブジェクト内でコピーされるか、文字列に対応する定数プールのアドレスに直接コピーされているかどうかをよりよく理解できます。
3。文字列オブジェクトのソースコードの一部:
<span style = "font-size:14pt">パブリックファイナルクラスの文字列は、java.io.serializable、comparable <string>、charquence { /**この値を文字ストレージに使用します。 */プライベートファイナルチャーバリュー[]; / **文字列のハッシュコードをキャッシュ*/ private int hash; //デフォルトは0 public string(){this.value = new char [0]; } </span> <span style = "background-color:#ffffff; font-size:18pt"> public string(string original){this.value = original.value; this.hash = original.hash; } </span>ソースコードから、文字列クラスにインスタンス変数char値[]があることがわかります。構築方法を通じて、オブジェクトは初期化時にコピー操作を実行せず、渡された文字列オブジェクトのアドレス参照をインスタンス変数値に割り当てることのみがわかります。このことから、最初に、新しい文字列(「ABC」)を使用して文字列オブジェクトを作成した場合でも、メモリヒープ内のオブジェクトにスペースが割り当てられますが、「ABC」自体に関する情報はヒープに保存されませんが、「ABC」文字列への参照はインスタンス変数内で「ABC」文字列に初期化されます。実際、これはメモリストレージスペースを節約し、プログラムのパフォーマンスを向上させることでもあります。
4.文字列オブジェクトのバイトコード情報を見てみましょう。
public java.lang.string();記述子:()vフラグ:acc_public code:stack = 2、locals = 1、args_size = 1 0:aload_0 1:invokespecial#1 // method java/lang/object。 "<init>" :()v 4:aload_0 5:iconst_0 6:newarray char 8:put#2 // 138行目:4行139:11 Public Java.lang.String(java.lang.String);記述子:( ljava/lang/string;)v flags:acc_public code:stack = 2、locals = 2、args_size = 2 0:aload_0 //ローカル変数0をスタックの上部に押します。 1:Invokespecial#1 // Method Java/Lang/Object。 "<init>" :()vスタックトップオブジェクトをポップアップして、オブジェクトの#1の初期化方法を参照します。 4:ALOAD_0 //独自のオブジェクトの参照をスタックの上部に押します。 5:ALOAD_1 //通過した文字列リファレンスは、スタックの上部にプッシュします。 6:GetField#2 //フィールド値:[c //スタックの上部に文字列リファレンスをポップアップし、#2のインスタンス変数に割り当て、スタックに保存します。 9:Putfield#2 //フィールド値:[c //スタックとオブジェクト自体の上部に文字列参照をポップアップし、オブジェクト自体のインスタンス変数に文字列参照を割り当てます。 12:ALOAD_0 13:ALOAD_1 14:GetField#3 // Field Hash:I 17:Putfield#3 // Field Hash:I 20:Return
bytecodeの観点から見ると、新しい文字列( "abc")は、文字列をコピーするのではなく、新しいオブジェクトを構築するときに文字列参照の割り当てを実行すると結論付けることができます。上記は、ソースコードとバイトコードの観点からの文字列のメモリ割り当ての分析と要約です。
Java文字列メモリ割り当て(推奨)の上記の分析と要約は、私があなたと共有するすべてのコンテンツです。参照を提供できることを願っています。wulin.comをもっとサポートできることを願っています。