国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

java 為什么需要常量池

Yuanf / 2973人閱讀

摘要:常量池探秘每個(gè)文件編譯為文件后,都將產(chǎn)生當(dāng)前類獨(dú)有的常量池,我們稱之為靜態(tài)常量池。文件中的常量池包含兩部分字面值和符號(hào)引用。方法的調(diào)用成員變量的訪問(wèn)最終都是通過(guò)運(yùn)行時(shí)常量池來(lái)查找具體地址的。其中,表示將一個(gè)常量加載到操作數(shù)棧。

java中講的常量池,通常指的是運(yùn)行時(shí)常量池,它是方法區(qū)的一部分,一個(gè)jvm實(shí)例只有一個(gè)運(yùn)行常量池,各線程間共享該運(yùn)行常量池。

java內(nèi)存模型中將內(nèi)存分為堆和棧,其中堆為線程間共享的內(nèi)存數(shù)據(jù)區(qū)域,棧為線程間私有的內(nèi)存區(qū)域。堆又包括方法區(qū)以及非方法區(qū)部分,棧包括本地方法棧、虛擬機(jī)棧等,如下圖所示:

為什么需要常量池

jvm 在棧幀(frame) 中進(jìn)行操作數(shù)和方法的動(dòng)態(tài)鏈接(link),為了便于鏈接,jvm 使用常量池來(lái)保存跟蹤當(dāng)前類中引用的其他類及其成員變量和成員方法。

每個(gè)棧幀(frame)都包含一個(gè)運(yùn)行常量池的引用,這個(gè)引用指向當(dāng)前棧幀需要執(zhí)行的方法,jvm使用這個(gè)引用來(lái)進(jìn)行動(dòng)態(tài)鏈接。

在 c/c++ 中,編譯器將多個(gè)編譯期編譯的文件鏈接成一個(gè)可執(zhí)行文件或者dll文件,在鏈接階段,符號(hào)引用被解析為實(shí)際地址。java 中這種鏈接是在程序運(yùn)行時(shí)動(dòng)態(tài)進(jìn)行的。

常量池探秘

每個(gè) java 文件編譯為 class 文件后,都將產(chǎn)生當(dāng)前類獨(dú)有的常量池,我們稱之為靜態(tài)常量池。class 文件中的常量池包含兩部分:字面值(literal)和符號(hào)引用(Symbolic Reference)。其中字面值可以理解為 java 中定義的字符串常量、final 常量等;符號(hào)引用指的是一些字符串,這些字符串表示當(dāng)前類引用的外部類、方法、變量等的引用地址的抽象表示形式,在類被jvm裝載并第一次使用這些符號(hào)引用時(shí),這些符號(hào)引用將會(huì)解析為直接引用。符號(hào)常量包含:

類和接口的全限定名

字段的名稱和描述符

方法的名稱和描述符

jvm在進(jìn)行類裝載時(shí),將class文件中常量池部分的常量加載到方法區(qū)中,此時(shí)方法區(qū)中的保存常量的邏輯區(qū)域稱之為運(yùn)行時(shí)常量區(qū)。

使用javap -verbose 命令可以查看class字節(jié)碼的詳細(xì)信息,其中包含了編譯期確定的靜態(tài)常量池。

public class StringTest {
    
    public static void main(String[] args){
        String s = new String("abc");
        String s2 = s.intern();
        System.out.println(s2 == s);

        String s3 = (s + s2);

        System.out.println(s3 == s3.intern());

    }
}

上述代碼javap -verbose后得到(只拿出常量池部分):

major version: 52
Constant pool:
   #1 = Methodref          #13.#26        // java/lang/Object."":()V
   #2 = Class              #27            // java/lang/String
   #3 = String             #28            // abc
   #4 = Methodref          #2.#29         // java/lang/String."":(Ljava/lang/String;)V
   #5 = Methodref          #2.#30         // java/lang/String.intern:()Ljava/lang/String;
   #6 = Fieldref           #31.#32        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = Methodref          #33.#34        // java/io/PrintStream.println:(Z)V
   #8 = Class              #35            // java/lang/StringBuilder
   #9 = Methodref          #8.#26         // java/lang/StringBuilder."":()V
  #10 = Methodref          #8.#36         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #11 = Methodref          #8.#37         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #12 = Class              #38            // StringTest
  #13 = Class              #39            // java/lang/Object
  #14 = Utf8               
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               main
  #19 = Utf8               ([Ljava/lang/String;)V
  #20 = Utf8               StackMapTable
  #21 = Class              #40            // "[Ljava/lang/String;"
  #22 = Class              #27            // java/lang/String
  #23 = Class              #41            // java/io/PrintStream
  #24 = Utf8               SourceFile
  #25 = Utf8               StringTest.java
  #26 = NameAndType        #14:#15        // "":()V
  #27 = Utf8               java/lang/String
  #28 = Utf8               abc
  #29 = NameAndType        #14:#42        // "":(Ljava/lang/String;)V
  #30 = NameAndType        #43:#44        // intern:()Ljava/lang/String;
  #31 = Class              #45            // java/lang/System
  #32 = NameAndType        #46:#47        // out:Ljava/io/PrintStream;
  #33 = Class              #41            // java/io/PrintStream
  #34 = NameAndType        #48:#49        // println:(Z)V
  #35 = Utf8               java/lang/StringBuilder
  #36 = NameAndType        #50:#51        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #37 = NameAndType        #52:#44        // toString:()Ljava/lang/String;
  #38 = Utf8               StringTest
  #39 = Utf8               java/lang/Object
  #40 = Utf8               [Ljava/lang/String;
  #41 = Utf8               java/io/PrintStream
  #42 = Utf8               (Ljava/lang/String;)V
  #43 = Utf8               intern
  #44 = Utf8               ()Ljava/lang/String;
  #45 = Utf8               java/lang/System
  #46 = Utf8               out
  #47 = Utf8               Ljava/io/PrintStream;
  #48 = Utf8               println
  #49 = Utf8               (Z)V
  #50 = Utf8               append
  #51 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #52 = Utf8               toString

我們可以看到,常量池共包含52個(gè)常量。#1 是一個(gè)類中方法的符號(hào)引用,它由 #13#26 兩個(gè)utf8編碼的字符串構(gòu)成;#3 是程序中定義的 String 類型的字面值 "abc",它包含指向一個(gè)utf8編碼字符串 "abc" 的索引 #28

方法的調(diào)用、成員變量的訪問(wèn)最終都是通過(guò)運(yùn)行時(shí)常量池來(lái)查找具體地址的。

String 常量池

運(yùn)行時(shí)常量池有一種 String 類型的常量,即通常我們所說(shuō)的字符串字面值,所有的字符串字面值組成一個(gè) String 常量表。String常量表并不是一成不變的,程序運(yùn)行時(shí)可以動(dòng)態(tài)添加字符串常量,使用String的intern()可以動(dòng)態(tài)的添加String常量。但

jvm 確保兩個(gè)在值上完全相等的字符串字面值(即其中包含的字符序列是相同的,使用equals()來(lái)判斷)指向同一個(gè) String 實(shí)例。

如:

String s1 = "abc";

String s2 = "abc";

System.out.println(s1 == s2); // true

上述代碼中的字符串 s1 和 s2 將指向同一個(gè) String 實(shí)例。實(shí)際上通過(guò)查看class文件,我們可以看到,在編譯后,靜態(tài)常量池中已經(jīng)包含了一個(gè) String 類型的字面值 "abc",程序運(yùn)行時(shí)只是從常量池中獲取這個(gè)String字面值的引用地址,并賦值給變量 s1 和變量 s2。

Constant pool:
   #1 = Methodref          #6.#19         // java/lang/Object."":()V
   #2 = String             #20            // abc
   ······
   #20 = Utf8               abc

public static void main(java.lang.String[]);
    ······
    Code:
      stack=3, locals=3, args_size=1
         0: ldc           #2                  // String abc
         2: astore_1
         3: ldc           #2                  // String abc

其中,ldc 表示將一個(gè)常量加載到操作數(shù)棧。

String 的 intern() 是一個(gè)native方法,返回的是一個(gè)String對(duì)象的標(biāo)準(zhǔn)表示。當(dāng)調(diào)用該方法時(shí),如果運(yùn)行時(shí)常量池中已經(jīng)存在與之相等(equal())的字符串,則直接返回常量池中的字符串引用,否則將此字符串添加到池中,并返回。

String s1 = "abc";

String s2 = new String("abc");

System.out.println(s1 == s2);           //返回 false

System.out.println(s1.equals(s2));      //返回 true

System.out.println(s1 == s2.intern());  //返回 true

上述代碼中,雖然 s1 和 s2 中的值是相同的,但是他們指向的并不是同一個(gè)對(duì)象,但 s2 的標(biāo)準(zhǔn)化表示和s1是同一個(gè) String 對(duì)象,都是編譯期確定的常量池中的 "abc"。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65567.html

相關(guān)文章

  • 方法區(qū)到底是個(gè)什么

    摘要:那方法區(qū)里都存著什么呢先拋結(jié)論靜態(tài)變量常量類信息構(gòu)造方法接口定義運(yùn)行時(shí)常量池存在方法區(qū)中。動(dòng)態(tài)常量池運(yùn)行時(shí)常量池是方法區(qū)的一部分,是一塊內(nèi)存區(qū)域。文件常量池將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。 一、方法區(qū)與永久代 這兩個(gè)是非常容易混淆的概念,永久代的對(duì)象放在方法區(qū)中,就會(huì)想當(dāng)然地認(rèn)為,方法區(qū)就等同于持久代的內(nèi)存區(qū)域。事實(shí)上兩者是這樣的關(guān)系: 《Java虛擬機(jī)規(guī)范》只是規(guī)定了有方...

    binaryTree 評(píng)論0 收藏0
  • String的內(nèi)存模型,什么String被設(shè)計(jì)成不可變的

    摘要:證明返回常量池中已存在的對(duì)象,不等于新建的對(duì)象。為什么要設(shè)計(jì)成一下內(nèi)容來(lái)自發(fā)現(xiàn)百度的中文版本基本也是此文的翻譯版。總之,安全性和字符串常量池緩存是被設(shè)計(jì)成不可變的主要原因。 String是Java中最常用的類,是不可變的(Immutable), 那么String是如何實(shí)現(xiàn)Immutable呢,String為什么要設(shè)計(jì)成不可變呢? 前言 關(guān)于String,收集一波基礎(chǔ),來(lái)源標(biāo)明最后,不確...

    vspiders 評(píng)論0 收藏0
  • 我終于搞清楚了和String有關(guān)的那點(diǎn)事兒。

    摘要:為了減少在中創(chuàng)建的字符串的數(shù)量,字符串類維護(hù)了一個(gè)字符串常量池。但是當(dāng)執(zhí)行了方法后,將指向字符串常量池中的那個(gè)字符串常量。由于和都是字符串常量池中的字面量的引用,所以。究其原因,是因?yàn)槌A砍匾4娴氖且汛_定的字面量值。 String,是Java中除了基本數(shù)據(jù)類型以外,最為重要的一個(gè)類型了。很多人會(huì)認(rèn)為他比較簡(jiǎn)單。但是和String有關(guān)的面試題有很多,下面我隨便找兩道面試題,看看你能不能...

    paulli3 評(píng)論0 收藏0
  • 可能是把Java內(nèi)存區(qū)域講的最清楚的一篇文章

    摘要:另外,為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器,各線程之間計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ),我們稱這類內(nèi)存區(qū)域?yàn)榫€程私有的內(nèi)存。運(yùn)行時(shí)常量池運(yùn)行時(shí)常量池是方法區(qū)的一部分。 寫在前面(常見面試題) 基本問(wèn)題: 介紹下 Java 內(nèi)存區(qū)域(運(yùn)行時(shí)數(shù)據(jù)區(qū)) Java 對(duì)象的創(chuàng)建過(guò)程(五步,建議能默寫出來(lái)并且要知道每一步虛擬機(jī)做了什么) 對(duì)象的訪問(wèn)定位的兩種方式(句...

    RobinQu 評(píng)論0 收藏0
  • java:String

    摘要:類是類它內(nèi)部的方法也默認(rèn)被修飾不能重寫字符串常量池當(dāng)這樣聲明一個(gè)字符串會(huì)檢測(cè)字符串常量池中是否存在這個(gè)值的字符串如果存在就直接賦值給否則創(chuàng)建一個(gè)新的再賦值給當(dāng)連續(xù)用同樣的方式聲明兩個(gè)字符串并作比較結(jié)果為這個(gè)操作符比較的是什么對(duì)于基本變量比較 String類是final類,它內(nèi)部的方法也默認(rèn)被final修飾,不能重寫. 字符串常量池 當(dāng)這樣聲明一個(gè)字符串 String str = h...

    U2FsdGVkX1x 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<