<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          寫一個(gè)java字符串時(shí),內(nèi)存是如何分配的?

          共 8237字,需瀏覽 17分鐘

           ·

          2020-08-29 02:37


          經(jīng)常在網(wǎng)上各大版塊都能看到對(duì)于java字符串運(yùn)行時(shí)內(nèi)存分配的探討,形如:String a = "123",String b = new String("123"),這兩種形式的字符串是存放在什么地方的呢,其實(shí)這兩種形式的字符串字面值"123"本身在運(yùn)行時(shí)既不是存放在棧上,也不是存放在堆上,他們是存放在方法區(qū)中的某個(gè)常量區(qū),并且對(duì)于相同的字符串字面值在內(nèi)存中只保留一份。下面我們將以實(shí)例來分析。


          1.==運(yùn)算符作用在兩個(gè)字符串引用比較的兩個(gè)案例:


          public?class?StringTest {
          ??public?static?void?main(String[] args) {
          ????//part 1
          ????String?s1 = "i love china";
          ????String?s2 = "i love china";
          ????System.out.println("result:"?+ s1 == s2);//程序運(yùn)行結(jié)果為true
          ????//part 2
          ????String?s3 = new?String("i love china");
          ????String?s4 = new?String("i love china");
          ????System.out.println("result:"?+ s3 == s4);//程序運(yùn)行結(jié)果為false
          ??}

          }


          我們知道java中==運(yùn)算符比較的是變量的值,對(duì)于引用類型對(duì)應(yīng)的變量的值存放的是引用對(duì)象的地址,在這里String是引用類型,這里面的四個(gè)變量的值存放的其實(shí)是指向字符串的地址。對(duì)于part2的執(zhí)行結(jié)果是顯然的,因?yàn)閚ew操作符會(huì)使jvm在運(yùn)行時(shí)在堆中創(chuàng)建新的對(duì)象,兩個(gè)不同的對(duì)象的地址是不同的。但是由part1的執(zhí)行結(jié)果,可以看出s1和s2是指向的同一個(gè)地址,那么由變量s1,s2指向的字符串是存放在什么地方的呢,jvm又是對(duì)字符串如何處理的呢。同樣的對(duì)于變量s3,s4所指向的堆中的不同的字符串對(duì)象,他們會(huì)分別在自己的對(duì)象空間中保存一份"i love china"字符串嗎,為了了解jvm是如何處理字符串,首先我們看java編譯器生成的字節(jié)碼指令。通過字節(jié)碼指令我們來分析jvm將會(huì)執(zhí)行哪些操作。


          2.以下為程序生成的部分字節(jié)碼信息。紅色標(biāo)注的是我們需要關(guān)注的部分。


          Constant pool:
          ??#1 = Class #2 // StringTest
          ??#2 = Utf8 StringTest
          ??#3 = Class #4 // java/lang/Object
          ??#4 = Utf8 java/lang/Object
          ??#5 = Utf8
          ??#6 = Utf8 ()V
          ??#7 = Utf8 Code
          ??#8 = Methodref #3.#9 // java/lang/Object."":()V
          ??#9 = NameAndType #5:#6 // "":()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 // i love china 字符串地址的引用
          ?#17 = Utf8 i love china
          ?#18 = Fieldref #19.#21 // java/lang/System.out:Ljava/io/PrintStream;
          ?#19 = Class #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 = Class #25 // java/lang/StringBuilder
          ?#25 = Utf8 java/lang/StringBuilder
          ?#26 = String #27 // result:
          ?#27 = Utf8 result:

          #
          28 = Methodref #24.#29 // java/lang/StringBuilder."":(Ljava/lang/String;)V
          #29 = NameAndType #5:#30 // "":(Ljava/lang/String;)V
          #30 = Utf8 (Ljava/lang/String;)V
          #31 = Methodref #24.#32 // java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
          #32 = NameAndType #33:#34 // append:(Z)Ljava/lang/StringBuilder;
          #33 = Utf8 append
          #34 = Utf8 (Z)Ljava/lang/StringBuilder;
          #35 = Methodref #24.#36 // java/lang/StringBuilder.toString:()Ljava/lang/String;
          #36 = NameAndType #37:#38 // toString:()Ljava/lang/String;
          #37 = Utf8 toString
          #38 = Utf8 ()Ljava/lang/String;
          #39 = Methodref #40.#42 // java/io/PrintStream.println:(Ljava/lang/String;)V
          #40 = Class #41 // java/io/PrintStream
          #41 = Utf8 java/io/PrintStream
          #42 = NameAndType #43:#30 // println:(Ljava/lang/String;)V
          #43 = Utf8 println
          #44 = Class #45 // java/lang/String
          #45 = Utf8 java/lang/String
          #46 = Methodref #44.#29 // java/lang/String."":(Ljava/lang/String;)V
          #47 = Utf8 args
          #48 = Utf8 [Ljava/lang/String;
          #49 = Utf8 s1
          #50 = Utf8 Ljava/lang/String;
          #51 = Utf8 s2
          #52 = Utf8 s3
          #53 = Utf8 s4
          #54 = Utf8 StackMapTable
          #55 = Class #48 // "[Ljava/lang/String;"
          #56 = Utf8 SourceFile
          #57 = Utf8 StringTest.java
          ...........
          //對(duì)應(yīng)的方法的字節(jié)碼指令,由jvm運(yùn)行時(shí)解釋執(zhí)行。
          ?public static void main(java.lang.String[]);
          ??descriptor: ([Ljava/lang/String;)V
          ??flags: ACC_PUBLIC, ACC_STATIC
          ??Code:
          ???stack=4, locals=5, args_size=1
          ?????0: ldc #16 // String i love china,該指令是將常量池的#16處符號(hào)引用,在這里為字符串“ilove china”符號(hào)引用push到棧頂。該指令與底下的指令2對(duì)應(yīng)于程序中的String s1 = "i love china"語句
          ?????2: astore_1 //將棧頂?shù)膶?duì)象引用賦值給局部變量1.
          ??? 3: ldc #16 // String i love china,同0處的指令,指向的是同一個(gè)符號(hào)引用處的常量。該指令與底下的指令5對(duì)應(yīng)于程序中的 String s2 = "i love china"語句。
          ?????5: astore_2 //將棧頂?shù)膶?duì)象引用賦值給局部變量2.
          ????6: getstatic #18 // Field java/lang/System.out:Ljava/io/PrintStream;
          ?????9: new #24 // class java/lang/StringBuilder
          ????12: dup
          ????13: ldc #26 // String result:
          ????15: invokespecial #28 // Method java/lang/StringBuilder."":(Ljava/lang/String;)V
          ????18: aload_1
          ????19: aload_2
          ????20: if_acmpne 27 //彈出棧頂兩個(gè)對(duì)象引用進(jìn)行比較其是否相等,不等,轉(zhuǎn)到指令27處,執(zhí)行,相等執(zhí)行下一條指令
          ????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 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          ??? 37: new #44 // class java/lang/String,創(chuàng)建一個(gè)對(duì)象,該對(duì)象位于常量池#44引用處,這里為String對(duì)象,并將對(duì)象引用push到棧頂。
          ??? 40: dup //拷貝棧頂一份對(duì)象引用push到棧頂。
          ??? 41: ldc #16 // String i love china,同0,3處指令。
          ????43: invokespecial #46 // Method java/lang/String."":(Ljava/lang/String;)V
          ????46: astore_3
          ????47: new #44 // class java/lang/String//創(chuàng)建一個(gè)對(duì)象,并將對(duì)象引用push到棧頂
          ????50: dup
          ??? 51: ldc #16 // String i love china, 將字符串的符號(hào)引用push到棧頂。
          ????53: invokespecial #46 // Method java/lang/String."":(Ljava/lang/String;)V,根據(jù)棧頂?shù)膶?duì)應(yīng)的對(duì)象引用及字符串引用調(diào)用對(duì)象的init初始化方法,對(duì)字符串對(duì)象初始化
          ????56: astore 4 //將棧頂對(duì)象引用賦值給變量4.
          ????58: getstatic #18 // Field java/lang/System.out:Ljava/io/PrintStream;
          ????61: new #24 // class java/lang/StringBuilder
          ????64: dup
          ????65: ldc #26 // String result:
          ????67: invokespecial #28 // Method java/lang/StringBuilder."":(Ljava/lang/String;)V
          ????70: aload_3
          ????71: aload 4
          ????73: if_acmpne 80
          ????76: iconst_1
          ????77: goto 81
          ????80: iconst_0
          ????81: invokevirtual #31 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;
          ????84: invokevirtual #35 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          ????87: invokevirtual #39 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          ????90: return
          .........


          LineNumberTable:
          line 7: 0
          line 8: 3
          line 9: 6
          line 11: 37
          line 12: 47
          line 13: 58
          line 14: 90
          LocalVariableTable:
          Start Length Slot Name Signature
          91 0 args [Ljava/lang/String;//局部變量0
          88 1 s1 Ljava/lang/String; //局部變量1
          85 2 s2 Ljava/lang/String;//局部變量2
          44 3 s3 Ljava/lang/String;//局部變量3
          33 4 s4 Ljava/lang/String;//局部變量4


          字節(jié)碼中紅色的部分是與我們討論相關(guān)的。通過生成的字節(jié)碼,我們可以對(duì)示例程序得出如下結(jié)論。


          1).? java編譯器在將程序編譯成字節(jié)碼的過程中,對(duì)遇到的字符串常量"i love china"首先判斷其是否在字節(jié)碼常量池中存在,不存在創(chuàng)建一份,存在的話則不創(chuàng)建,也就是相等的字符串,只保留一份,通過符號(hào)引用可以找到它,這樣使得程序中的字符串變量s1和s2都是指向常量池中的同一個(gè)字符串常量。在運(yùn)行時(shí)jvm會(huì)將字節(jié)碼常量池中的字符串常量存放在方法區(qū)中的通常稱之為常量池的位置,并且字符串是以字符數(shù)組的形式通過索引來訪問的。jvm在運(yùn)行時(shí)將s1與s2指向的字符串相對(duì)引用地址指向字符串實(shí)際的內(nèi)存地址。


          2).? 對(duì)于String s3 = new String("i love china"),String s4 = new String("i love china"),由字節(jié)碼可以看出其是調(diào)用了new指令,jvm會(huì)在運(yùn)行時(shí)創(chuàng)建兩個(gè)不同的對(duì)象,s3與s4指向的是不同的對(duì)象地址。所以s3==s4比較的結(jié)果為false。

          其次,對(duì)于s3與s4對(duì)象的初始化,從字節(jié)碼看出是調(diào)用對(duì)象的init方法并且傳遞的是常量池中”i love china”的引用,那么創(chuàng)建String對(duì)象及初始化究竟干了什么,我們可以查看通過查看String的源碼及String對(duì)象生成的字節(jié)碼,以便更好的了解對(duì)于new String("i love china")時(shí),在對(duì)象內(nèi)部是做了字符串的拷貝還是直接指向該字符串對(duì)應(yīng)的常量池的地址的引用。


          3.String對(duì)象的部分源碼:


          "FONT-SIZE: 14pt">public?final?class?String?
          ??implements?java.io.Serializable, Comparable<String>, CharSequence?
          {
          ??/** The value is used for character storage. */
          ??private?final?char value[];
          ?
          ??/** Cache the hash code for the string */
          ??private?int hash; // Default to 0
          ?
          ??public?String() {
          ????this.value = new?char[0];
          ??}
          ??"BACKGROUND-COLOR: #ffffff; FONT-SIZE: 18pt"> public?String(String original) {
          ????this.value = original.value;
          ????this.hash = original.hash;
          ??}


          從源碼中我們看到String類里有個(gè)實(shí)例變量 char value[],通過構(gòu)造方法我們可知,對(duì)象在初始化時(shí)并沒有做拷貝操作,只是將傳遞進(jìn)來的字符串對(duì)象的地址引用賦給了實(shí)例變量value。由此我們可以初步的得出結(jié)論:即使使用new String("abc")創(chuàng)建了一個(gè)字符串對(duì)象時(shí),在內(nèi)存堆中為該對(duì)象分配了空間,但是在堆上并沒有存儲(chǔ)"abc"本身的任何信息,只是初始化了其內(nèi)部的實(shí)例變量到"abc"字符串的引用。其實(shí)這樣做也是為了節(jié)省內(nèi)存的存儲(chǔ)空間,以及提高程序的性能。


          4.下面我們來看看String對(duì)象部分字節(jié)碼信息:


          public?java.lang.String();
          ??descriptor: ()V
          ??flags: ACC_PUBLIC
          ??Code:
          ???stack=2, locals=1, args_size=1
          ?????0: aload_0
          ?????1: invokespecial #1?????????// Method java/lang/Object."":()V
          ?????4: aload_0
          ?????5: iconst_0
          ?????6: newarray char
          ?????8: putfield #2?????????// Field value:[C
          ????11: return
          ???LineNumberTable:
          ????line 137: 0
          ????line 138: 4
          ????line 139: 11

          ?public?java.lang.String(java.lang.String);
          ??descriptor: (Ljava/lang/String;)V
          ??flags: ACC_PUBLIC
          ??Code:
          ???stack=2, locals=2, args_size=2
          ?????0: aload_0 //將局部變量0push到棧頂,自身對(duì)象的引用。
          ?????1: invokespecial #1?????????// Method java/lang/Object."":()V 彈出棧頂對(duì)象引用調(diào)用該對(duì)象的#1處的初始化方法。
          ?????4: aload_0 //將自身對(duì)象引用push到棧頂。
          ?????5: aload_1 //傳遞的字符串引用push到棧頂。
          ?????6: getfield #2?????????// Field value:[C // 彈出棧頂?shù)淖址貌⑵滟x值給#2處的實(shí)例變量,并將其存放到棧上。
          ?????9: putfield #2?????????// Field value:[C // 彈出棧頂?shù)淖址眉皩?duì)象自身的引用并將字符串的引用賦值給本對(duì)象自身的實(shí)例變量。
          ????12: aload_0
          ????13: aload_1
          ????14: getfield #3?????????// Field hash:I
          ????17: putfield #3?????????// Field hash:I
          ????20: return


          從字節(jié)碼的角度我們可以得出結(jié)論,new String("abc")在構(gòu)造新對(duì)象時(shí)執(zhí)行的是字符串引用的賦值,而不是字符串的拷貝。以上是從源碼及字節(jié)碼的角度來對(duì)字符串的內(nèi)存分配進(jìn)行的分析與總結(jié)。


          以上這篇java 字符串內(nèi)存分配的分析與總結(jié)(推薦)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。


          原文鏈接:www.jb51.net/article/90727.htm



          瀏覽 48
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费看黄色视频在线观看 | 欧美成人黄色电影网站 | 伊人成年网| 五月天无码在线 | 国产一级a毛一级a在线 |