最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java 字符串內(nèi)存分配的分析與總結(jié)(推薦)

 更新時(shí)間:2016年08月18日 09:40:15   投稿:jingxian  
下面小編就為大家?guī)?lái)一篇java 字符串內(nèi)存分配的分析與總結(jié)(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

經(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í)例來(lái)分析。

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é)碼指令。通過(guò)字節(jié)碼指令我們來(lái)分析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        <init>
  #6 = Utf8        ()V
  #7 = Utf8        Code
  #8 = Methodref     #3.#9     // java/lang/Object."<init>":()V
  #9 = NameAndType    #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      // 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."<init>":(Ljava/lang/String;)V
#29 = NameAndType #5:#30 // "<init>":(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."<init>":(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"語(yǔ)句
     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"語(yǔ)句。
     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."<init>":(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."<init>":(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."<init>":(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."<init>":(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)的。通過(guò)生成的字節(jié)碼,我們可以對(duì)示例程序得出如下結(jié)論。

1).  java編譯器在將程序編譯成字節(jié)碼的過(guò)程中,對(duì)遇到的字符串常量"i love china"首先判斷其是否在字節(jié)碼常量池中存在,不存在創(chuàng)建一份,存在的話則不創(chuàng)建,也就是相等的字符串,只保留一份,通過(guò)符號(hào)引用可以找到它,這樣使得程序中的字符串變量s1和s2都是指向常量池中的同一個(gè)字符串常量。在運(yùn)行時(shí)jvm會(huì)將字節(jié)碼常量池中的字符串常量存放在方法區(qū)中的通常稱之為常量池的位置,并且字符串是以字符數(shù)組的形式通過(guò)索引來(lái)訪問(wèn)的。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ì)象及初始化究竟干了什么,我們可以查看通過(guò)查看String的源碼及String對(duì)象生成的字節(jié)碼,以便更好的了解對(duì)于new String("i love china")時(shí),在對(duì)象內(nèi)部是做了字符串的拷貝還是直接指向該字符串對(duì)應(yīng)的常量池的地址的引用。

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

<SPAN style="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]; 
  }</SPAN> 
  <SPAN style="BACKGROUND-COLOR: #ffffff; FONT-SIZE: 18pt"> public String(String original) { 
    this.value = original.value; 
    this.hash = original.hash; 
  } 
</SPAN>

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

4.下面我們來(lái)看看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."<init>":()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."<init>":()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é)碼的角度來(lái)對(duì)字符串的內(nèi)存分配進(jìn)行的分析與總結(jié)。

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

相關(guān)文章

  • Java Scoket實(shí)現(xiàn)雙向通信代碼詳解

    Java Scoket實(shí)現(xiàn)雙向通信代碼詳解

    這篇文章主要介紹了Java Scoket實(shí)現(xiàn)雙向通信代碼詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 解決IDEA克隆代碼后在右下角沒(méi)有g(shù)it分支的問(wèn)題

    解決IDEA克隆代碼后在右下角沒(méi)有g(shù)it分支的問(wèn)題

    這篇文章主要介紹了解決IDEA克隆代碼后在右下角沒(méi)有g(shù)it分支的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • Java中調(diào)用第三方接口的幾種方法詳細(xì)指南

    Java中調(diào)用第三方接口的幾種方法詳細(xì)指南

    在Java開(kāi)發(fā)中調(diào)用第三方接口是常見(jiàn)需求,本文介紹如何使用Java進(jìn)行接口調(diào)用,重點(diǎn)講解HttpURLConnection類、OkHttp庫(kù)和ApacheHttpClient的使用,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • java中的可變參數(shù)使用方法

    java中的可變參數(shù)使用方法

    這篇文章主要介紹了java中的可變參數(shù)使用方法的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • 詳解Java分布式緩存系統(tǒng)中必須解決的四大問(wèn)題

    詳解Java分布式緩存系統(tǒng)中必須解決的四大問(wèn)題

    分布式緩存系統(tǒng)是三高架構(gòu)中不可或缺的部分,極大地提高了整個(gè)項(xiàng)目的并發(fā)量、響應(yīng)速度,但它也帶來(lái)了新的需要解決的問(wèn)題,分別是: 緩存穿透、緩存擊穿、緩存雪崩和緩存一致性問(wèn)題。本文將詳細(xì)講解一下這四大問(wèn)題,需要的可以參考一下
    2022-04-04
  • Java中全局變量和局部變量詳解(看這篇就夠了)

    Java中全局變量和局部變量詳解(看這篇就夠了)

    在Java中全局變量和局部變量是兩種不同作用域的變量,這篇文章主要給大家介紹了關(guān)于Java中全局變量和局部變量的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),大家看這篇就夠了,需要的朋友可以參考下
    2023-11-11
  • 整理Java的MyBatis框架中一些重要的功能及基本使用示例

    整理Java的MyBatis框架中一些重要的功能及基本使用示例

    這篇文章主要介紹了Java的MyBatis框架中一些重要的功能及基本使用示例整理,MyBatis可以幫助Java程序進(jìn)行強(qiáng)大的數(shù)據(jù)庫(kù)操作,需要的朋友可以參考下
    2016-04-04
  • 淺析Spring配置文件

    淺析Spring配置文件

    本文主要對(duì)Spring配置文件進(jìn)行了介紹。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • mybatis使用collection嵌套查詢的實(shí)現(xiàn)

    mybatis使用collection嵌套查詢的實(shí)現(xiàn)

    本文主要介紹了mybatis使用collection嵌套查詢的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • 你一定不知道的Java Unsafe用法詳解

    你一定不知道的Java Unsafe用法詳解

    Unsafe是位于sun.misc包下的一個(gè)類,主要提供一些用于執(zhí)行低級(jí)別、不安全操作的方法,如直接訪問(wèn)系統(tǒng)內(nèi)存資源、自主管理內(nèi)存資源等,下面這篇文章主要給大家介紹了關(guān)于Java Unsafe用法的相關(guān)資料,需要的朋友可以參考下
    2021-10-10

最新評(píng)論

普兰县| 枝江市| 淄博市| 喀喇沁旗| 彰化县| 扎鲁特旗| 含山县| 安远县| 德清县| 芜湖市| 秦安县| 龙口市| 如东县| 绵阳市| 鄄城县| 肃南| 湖北省| 麻阳| 包头市| 高雄市| 庄河市| 信丰县| 商城县| 平昌县| 宁都县| 宁陵县| 沁阳市| 高唐县| 毕节市| 敖汉旗| 正镶白旗| 南京市| 建阳市| 柳林县| 米泉市| 雷波县| 宕昌县| 温泉县| 湖北省| 阿荣旗| 苗栗县|