成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

在Java虛擬機(jī)中,字符串常量到底存放在哪

lewinlee / 1615人閱讀

摘要:的三種常量池此外,有三種常量池,即字符串常量池又叫全局字符串池文件常量池運(yùn)行時(shí)常量池。開始虛擬機(jī)把字符串常量池位置從永久代挪到堆,又徹底取消,把諸如之類的元數(shù)據(jù)都挪到堆之外管理。

前言

前陣子和朋友討論一個(gè)問題:

字符串常量歸常量池管理,那比如 String str = "abc"; 
"abc"這個(gè)對象是放在內(nèi)存中的哪個(gè)位置,是字符串常量池中還是堆?

”這句代碼的abc當(dāng)然在常量池中,只有new String("abc")這個(gè)對象才在堆中創(chuàng)建“,他們大概是這么回答。

“abc”這個(gè)東西,是放在常量池中,這個(gè)答案是錯(cuò)誤的。

字符串“abc"的本體、實(shí)例,應(yīng)該是存在于Java堆中。

可能還真的有部分同學(xué)對這個(gè)知識(shí)點(diǎn)不熟悉,今天和大家聊聊字符串這個(gè)問題 ~

初學(xué)Java時(shí),學(xué)到字符串這一部分,有一段代碼

String str1 = "hello";
String str2 = new String("hello"); 
書上的解釋是:執(zhí)行第一行的時(shí)候,已經(jīng)把"hello"字符串放到了常量池中,執(zhí)行第二行代碼時(shí),會(huì)將常量池中已經(jīng)存在的"hello"復(fù)制一份到堆內(nèi)存中,創(chuàng)建一個(gè)的新的String對象。雖然值一樣,但他們是不同的對象。

當(dāng)時(shí)看完這個(gè)解釋,我產(chǎn)生了很多疑惑。因?yàn)樵诖酥耙呀?jīng)知道字符串的底層是char數(shù)組實(shí)現(xiàn)的。我很疑惑:

他copy一份過去,是copy了char數(shù)組呢?

還是copy整個(gè)String對象?

"hello" 這個(gè)對象實(shí)例真的存放在常量池中嗎?

當(dāng)時(shí)在網(wǎng)上搜了一些文章和答案,各有說辭,大部分回答都是 "str" 這個(gè)對象在常量池中,但也有認(rèn)為字符串常量實(shí)例(或叫對象)是在堆中創(chuàng)建,只是將其引用放到字符串常量池中,交給常量池管理。

JAVA內(nèi)存區(qū)域 — 運(yùn)行時(shí)數(shù)據(jù)區(qū)

理清這個(gè)問題前,需要梳理一下前置知識(shí)。

從一個(gè)經(jīng)典的示意圖講起,以hotspot虛擬機(jī)為例,此內(nèi)存模型需建立在JDK1.7之前的版本來討論,JDK1.7之后有所改變,但是原理還是一樣的。

Java虛擬機(jī)管理的內(nèi)存是運(yùn)行時(shí)數(shù)據(jù)區(qū)那一部分,簡單概括一下其中各個(gè)區(qū)域的區(qū)別:

虛擬機(jī)棧:線程私有,生命周期與線程相同,即每條線程都一個(gè)獨(dú)立的棧(VM Stack)。每個(gè)方法執(zhí)行時(shí)都會(huì)創(chuàng)建一個(gè)棧幀,也就是說,當(dāng)有一條線程執(zhí)行了多個(gè)方法時(shí),就會(huì)有一個(gè)棧,棧中有多個(gè)棧幀。

本地方法棧:線程私有

程序計(jì)數(shù)器:線程私有

堆Heap:線程共享,是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實(shí)例,幾乎所有的對象實(shí)例都在這里分配內(nèi)存。在Java虛擬機(jī)規(guī)范中的描述是:所有的對象實(shí)例以及數(shù)組都要在堆上分配。 (原文:The heap is the runtime data area from which memory for all class instances and arrays is allocated) 但有特殊情況,隨著JIT編譯器的發(fā)展,逃逸分析和標(biāo)量替換技術(shù)的逐漸成熟,對象也可以在棧上分配。另外,雖說堆是線程共享,但其中也可以劃分出多個(gè)線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer,TLAB)。

方法區(qū):線程共享,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。

JAVA的三種常量池

此外,Java有三種常量池,即字符串常量池(又叫全局字符串池)、class文件常量池、運(yùn)行時(shí)常量池。

? (圖一)

1. 字符串常量池(也叫全局字符串池、string pool、string literal pool)

字符串常量池在每個(gè)VM中只有一份,他在內(nèi)存中的位置如圖,紅色箭頭所指向的區(qū)域 Interned Strings

2. 運(yùn)行時(shí)常量池(runtime constant pool)

當(dāng)程序運(yùn)行到某個(gè)類時(shí),class文件中的信息就會(huì)被解析到內(nèi)存的方法區(qū)里的運(yùn)行時(shí)常量池中。看圖可清晰感知到每一個(gè)類被加載進(jìn)來都會(huì)產(chǎn)生一個(gè)運(yùn)行時(shí)常量池,由此可知,每個(gè)類都有一個(gè)運(yùn)行時(shí)常量池。它在內(nèi)存中的位置如圖,藍(lán)色箭頭所指向的區(qū)域,方法區(qū)中的Class Date中的運(yùn)行時(shí)常量池(Run-Time Constant Pool)

? (圖二)

3. class文件常量池(class constant pool)

class常量池是在編譯后每個(gè)class文件都有的,class文件中除了包含類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息就是 常量池(constant pool table),用于存放編譯器生成的各種字面量(Literal)和符號(hào)引用(Symbolic References)。字面量就是我們所說的常量概念,如文本字符串、被聲明為final的常量值等。他在class文件中的位置如上圖所示,Constant Pool 中。

個(gè)人理解
public static void main(String[] args) {
    String str = "hello";
}

回到一開始說到的這句代碼,可以來總結(jié)一下它的執(zhí)行過程了。

首先,字面量 "hello" 在編譯期,就會(huì)被記錄在 class文件的 class常量池中。

而當(dāng) class文件被加載到內(nèi)存中后,JVM就會(huì)將 class常量池中的大部分內(nèi)容存放到運(yùn)行時(shí)常量池中,但是字符串 "hello" 的本體(對象)和其他所有對象一樣,是會(huì)在堆中創(chuàng)建,再將引用放到字符串常量池,也就是圖一的 Interned Strings的位置。(RednaxelaFX 的文章里,測試結(jié)果是在新生代的Eden區(qū)。但因?yàn)橐恢庇幸粋€(gè)引用駐留在字符串常量池,所以不會(huì)被GC清理掉)

而到了String str = "hello" 這步,JVM會(huì)去字符串常量池中找,如果找到了,JVM會(huì)在棧中的局部變量表里創(chuàng)建str變量,然后把字符串常量池中的(hello 對象的)引用復(fù)制給 str 變量。

在《深入理解Java虛擬機(jī)》這本書中也有字符串相關(guān)的解釋,舉其中幾個(gè)例子:

例子1

(原文)運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息是常量池(Constant Pool Table),用于存放編譯期生成的各種字面量和符號(hào)引用,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。

最后一句描述不太準(zhǔn)確,編譯期生成的各種字面量并不是全部進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中。字符串字面量就不進(jìn)入運(yùn)行時(shí)常量池,而是在堆中創(chuàng)建了對象再將引用駐留到字符串常量池中。

例子2

代碼清單2-7 String.intern()返回引用的測試

public class RuntimeConstantPoolOOM{

    public static void main(String[]args){
        String str1=new StringBuilder("計(jì)算機(jī)").append("軟件").toString();
        System.out.println(str1.intern()==str1);
        String str2=new StringBuilder("ja").append("va").toString();
        System.out.println(str2.intern()==str2);
    }
}
(原文)這段代碼在JDK 1.6中運(yùn)行,會(huì)得到兩個(gè)false,而在JDK 1.7中運(yùn)行,會(huì)得到一個(gè)true和一個(gè)false。產(chǎn)生差異的原因是:在JDK 1.6中,intern()方法會(huì)把首次遇到的字符串實(shí)例復(fù)制到永久代中,返回的也是永久代中這個(gè)字符串實(shí)例的引用,而由StringBuilder創(chuàng)建的字符串實(shí)例在Java堆上,所以必然不是同一個(gè)引用,將返回false。而JDK  1.7(以及部分其他虛擬機(jī),例如JRockit)的intern()實(shí)現(xiàn)不會(huì)再復(fù)制實(shí)例,只是在常量池中記錄首次出現(xiàn)的實(shí)例引用,因此intern()返回的引用和由StringBuilder創(chuàng)建的那個(gè)字符串實(shí)例是同一個(gè)。對str2比較返回false是因?yàn)?“java” 這個(gè)字符串在執(zhí)行StringBuilder.toString()之前已經(jīng)出現(xiàn)過,字符串常量池中已經(jīng)有它的引用了,不符合“首次出現(xiàn)”的原則,而“計(jì)算機(jī)軟件”這個(gè)字符串則是首次出現(xiàn)的,因此返回true。

原文解釋也不太準(zhǔn)確,我覺得在 JDK 1.6中,intern()并不會(huì)把首次遇到的字符串實(shí)例復(fù)制到永久代中,而是會(huì)將實(shí)例再復(fù)制一份到堆(heap)中,然后將其引用放入字符串常量池中進(jìn)行管理,所以此代碼返回false。而JDK1.7中的intern()不會(huì)再復(fù)制實(shí)例,直接將首次遇到的此字符串實(shí)例的引用,放入字符串常量池,于是返回true。關(guān)于此觀點(diǎn),還沒看到大神文章實(shí)錘,歡迎討論。

最后再延伸一點(diǎn),大家都知道,字符串的value是final修飾的char數(shù)組,那么以下這段代碼:

// private final char value[];
String str1 = "hello world";
String str2 = new String("hello world");
String str3 = new String("hello world");

str1、str2、str3 三個(gè)變量所指向的都是不同的對象。(str1 != str2 != str3)

那么,這三個(gè)對象里的char數(shù)組是否是同一個(gè)數(shù)組?相信大家都有答案了。

此文所討論的Java內(nèi)存模型是建立在JDK1.7之前。JDK 7開始 Hotspot 虛擬機(jī)把字符串常量池(Interned String位置)從永久代(PermGen)挪到Heap堆,JDK 8又徹底取消 PermGen,把諸如klass之類的元數(shù)據(jù)都挪到GC堆之外管理。但不管怎樣,基本原理還是不變的,字面量 ”hello“ 等依舊不是放在 Interned String 中。

推薦文章:

請別再拿 “String s = new String("xyz"); 創(chuàng)建了多少個(gè)String實(shí)例” 來面試了吧

借HSDB來探索HotSpot VM的運(yùn)行時(shí)數(shù)據(jù)

作者:RednaxelaFX,曾為《深入理解Java虛擬機(jī)》提推薦語

java用這樣的方式生成字符串:String str = "Hello",到底有沒有在堆中創(chuàng)建對象? - 胖君的回答 - 知乎

隆鵬

廣州蘆葦科技Java開發(fā)團(tuán)隊(duì)

蘆葦科技-廣州專業(yè)互聯(lián)網(wǎng)軟件服務(wù)公司

抓住每一處細(xì)節(jié) ,創(chuàng)造每一個(gè)美好

關(guān)注我們的公眾號(hào),了解更多

想和我們一起奮斗嗎?lagou搜索“ 蘆葦科技 ”或者投放簡歷到 server@talkmoney.cn 加入我們吧

關(guān)注我們,你的評論和點(diǎn)贊對我們最大的支持

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

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/73095.html

相關(guān)文章

  • JVM詳解1.Java內(nèi)存模型

    摘要:編譯參見深入理解虛擬機(jī)節(jié)走進(jìn)之一自己編譯源碼內(nèi)存模型運(yùn)行時(shí)數(shù)據(jù)區(qū)域根據(jù)虛擬機(jī)規(guī)范的規(guī)定,的內(nèi)存包括以下幾個(gè)運(yùn)運(yùn)行時(shí)數(shù)據(jù)區(qū)域程序計(jì)數(shù)器程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,他可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器。 點(diǎn)擊進(jìn)入我的博客 1.1 基礎(chǔ)知識(shí) 1.1.1 一些基本概念 JDK(Java Development Kit):Java語言、Java虛擬機(jī)、Java API類庫JRE(...

    TANKING 評論0 收藏0
  • 深度理解JVM-----運(yùn)行時(shí)數(shù)據(jù)區(qū)域

    摘要:在之后,原來永久代的數(shù)據(jù)被分到了堆和元空間中。元空間存儲(chǔ)類的元信息,靜態(tài)變量和常量池等放入堆中。這樣能在一些場景中顯著提高性能,因?yàn)楸苊饬嗽诙褍?nèi)存和堆外內(nèi)存來回拷貝數(shù)據(jù)。 以下內(nèi)容部分轉(zhuǎn)載于: CS-Notes showImg(http://ww1.sinaimg.cn/large/005NT19Ply1g385uooqv9j30kd0slmyw.jpg); 程序計(jì)數(shù)器(Program...

    tuantuan 評論0 收藏0
  • Java 內(nèi)存區(qū)域詳解

    摘要:三對象的內(nèi)存布局對象在堆中的布局分為三個(gè)區(qū)域?qū)ο箢^,實(shí)例數(shù)據(jù),對齊填充??偨Y(jié)了解內(nèi)存區(qū)域是對的深入學(xué)習(xí),以前只知道有堆和棧的區(qū)分,現(xiàn)在我們了解到了具體的堆棧的作用。 引言 學(xué)習(xí)Java也有一段時(shí)間了,總感覺有些東西學(xué)的不是很精通。例如Java內(nèi)存區(qū)域到底是怎么樣的?程序是怎么跑的?對象是怎么存放的?這些都影響了我對自己的程序運(yùn)行的熟悉程度。 一. 運(yùn)行時(shí)數(shù)據(jù)區(qū)域 showImg(/im...

    darry 評論0 收藏0
  • Java內(nèi)存區(qū)域劃分和內(nèi)存分配

    摘要:運(yùn)行時(shí)數(shù)據(jù)區(qū)域虛擬機(jī)在執(zhí)行的過程中會(huì)把管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域。方法區(qū)的內(nèi)存收集還是會(huì)出現(xiàn),不過這個(gè)區(qū)域的內(nèi)存收集主要是針對常量池的回收和對類型的卸載。當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時(shí)將拋出異常。 運(yùn)行時(shí)數(shù)據(jù)區(qū)域Java虛擬機(jī)在執(zhí)行Java的過程中會(huì)把管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域。這些區(qū)域有各自的用途,以及創(chuàng)建和銷毀的時(shí)間,有的區(qū)域隨著虛擬機(jī)進(jìn)程的啟動(dòng)而存在,而有的區(qū)...

    BDEEFE 評論0 收藏0
  • jvm基礎(chǔ)篇一之內(nèi)存區(qū)域

    摘要:堆區(qū)堆是虛擬機(jī)所管理的內(nèi)存中最大的一塊,它是被所有線程共享的一塊內(nèi)存區(qū)域,該區(qū)域在虛擬機(jī)啟動(dòng)的時(shí)候創(chuàng)建。 運(yùn)行時(shí)數(shù)據(jù)區(qū)域 ? ?想要了解jvm,那對其內(nèi)存分配管理的學(xué)習(xí)是必不可少的;java虛擬機(jī)在執(zhí)行java程序的時(shí)候會(huì)把它所管理的內(nèi)存劃分成若干數(shù)據(jù)區(qū)域。這些區(qū)域有著不同的功能、用途、創(chuàng)建/銷毀時(shí)間。java虛擬機(jī)所分配管理的內(nèi)存區(qū)域如圖1所示 程序計(jì)數(shù)器 ? ?程序計(jì)數(shù)器是一塊比較...

    Zachary 評論0 收藏0

發(fā)表評論

0條評論

lewinlee

|高級講師

TA的文章

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