欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

面試時(shí)必問的JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)詳解

 更新時(shí)間:2021年08月16日 14:46:14   作者:程序員囧輝  
這篇文章主要介紹了JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

前言

Java 虛擬機(jī)的運(yùn)行時(shí)數(shù)據(jù)區(qū)經(jīng)常在面試中被拿來提問,很多概念在市面上有各種各樣的說法,搞的不少同學(xué)應(yīng)該是懵逼的。

當(dāng)我們陷入不知道哪個(gè)說法是正確的情況時(shí),最好的參考就是源碼和規(guī)范。

在面試中,當(dāng)面試官反問你:為什么某某是這樣?的時(shí)候,如果你回答:因?yàn)橐?guī)范是這么寫的、因?yàn)樵创a是這么寫的。

這個(gè)回答是非常有說服力的。

因此,本文在描述一些有爭(zhēng)議的問題上,優(yōu)先以《Java 虛擬機(jī)規(guī)范》的說法為準(zhǔn)。

正文

1、運(yùn)行時(shí)數(shù)據(jù)區(qū)(Run-Time Data Areas)

Java 虛擬機(jī)定義了若干種在程序執(zhí)行期間會(huì)使用到的運(yùn)行時(shí)數(shù)據(jù)區(qū)域。

其中一些數(shù)據(jù)區(qū)域在 Java 虛擬機(jī)啟動(dòng)時(shí)被創(chuàng)建,隨著虛擬機(jī)退出而銷毀。也就是線程間共享的區(qū)域:堆、方法區(qū)、運(yùn)行時(shí)常量池。

另外一些數(shù)據(jù)區(qū)域是按線程劃分的,這些數(shù)據(jù)區(qū)域在線程創(chuàng)建時(shí)創(chuàng)建,在線程退出時(shí)銷毀。也就是線程間隔離的區(qū)域:程序計(jì)數(shù)器、Java虛擬機(jī)棧、本地方法棧。

1)程序計(jì)數(shù)器(Program Counter Register)

Java 虛擬機(jī)可以支持多個(gè)線程同時(shí)執(zhí)行,每個(gè)線程都有自己的程序計(jì)數(shù)器。在任何時(shí)刻,每個(gè)線程都只會(huì)執(zhí)行一個(gè)方法的代碼,這個(gè)方法稱為該線程的當(dāng)前方法(current method)。

如果線程正在執(zhí)行的是 Java 方法(不是 native 的),則程序計(jì)數(shù)器記錄的是正在執(zhí)行的 Java 虛擬機(jī)字節(jié)碼指令的地址。如果正在執(zhí)行的是本地(native)方法,那么計(jì)數(shù)器的值是空的(undefined)。

2)Java虛擬機(jī)棧(Java Virtual Machine Stacks)

每個(gè) Java 虛擬機(jī)線程都有自己私有的 Java 虛擬機(jī)棧,它與線程同時(shí)創(chuàng)建,用于存儲(chǔ)棧幀。

Java 虛擬機(jī)棧描述的是 Java 方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。

每一個(gè)方法從調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程。

3)本地方法棧(Native Method Stacks)

本地方法棧與 Java 虛擬機(jī)棧所發(fā)揮的作用是非常相似的,它們之間的區(qū)別不過是 Java 虛擬機(jī)棧為虛擬機(jī)執(zhí)行 Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則為虛擬機(jī)使用到的本地(Native)方法服務(wù)。

4)堆(Heap)

堆是被各個(gè)線程共享的運(yùn)行時(shí)內(nèi)存區(qū)域,也是供所有類實(shí)例和數(shù)組對(duì)象分配內(nèi)存的區(qū)域。

堆在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,堆存儲(chǔ)的對(duì)象不會(huì)被顯示釋放,而是由垃圾收集器進(jìn)行統(tǒng)一管理和回收。

5)方法區(qū)(Method Area)

方法區(qū)是被各個(gè)線程共享的運(yùn)行時(shí)內(nèi)存區(qū)域。方法區(qū)類似于傳統(tǒng)語言的編譯代碼的存儲(chǔ)區(qū)。它存儲(chǔ)了每一個(gè)類的結(jié)構(gòu)信息,例如:運(yùn)行時(shí)常量池、字段和方法數(shù)據(jù),構(gòu)造函數(shù)和普通方法的字節(jié)碼內(nèi)容,還包括一些用于類、實(shí)例、接口初始化用到的特殊方法。

6)運(yùn)行時(shí)常量池(Run-Time Constant Pool)

運(yùn)行時(shí)常量池是 class 文件中每一個(gè)類或接口的常量池表(constant_pool table)的運(yùn)行時(shí)表示形式。

它包含了若干種常量,從編譯時(shí)已知的數(shù)值字面量到必須在運(yùn)行時(shí)解析后才能獲得的方法和字段引用。運(yùn)行時(shí)常量池的功能類似于傳統(tǒng)編程語言的符號(hào)表(symbol table),不過它包含的數(shù)據(jù)范圍比通常意義上的符號(hào)表要更為廣泛。

2、Java 中有哪幾種常量池?

現(xiàn)在我們經(jīng)常提到的常量池主要有三種:class 文件常量池、運(yùn)行時(shí)常量池、字符串常量池。

3、class 文件常量池

class 文件常量池(class constant pool)屬于 class 文件的其中一項(xiàng),class 類文件包含:魔數(shù)、類的版本、常量池、訪問標(biāo)志、字段表集合、方發(fā)表等信息。

常量池用于存放編譯期間生成的各種字面量(Literal)和符號(hào)引用(Symbolic References)。

字面量比較接近于Java語言層面的常量概念,如文本字符串、聲明為 final 的常量值等。

符號(hào)引用則屬于編譯原理方面的概念。符號(hào)引用是一組符號(hào)來描述所引用的目標(biāo),符號(hào)可以是任何形式的字面量,只要使用時(shí)能無歧義地定位到目標(biāo)即可(它與直接引用區(qū)分,直接引用一般是指向方法區(qū)的本地指針,相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄)。符號(hào)引用主要包括下面幾類常量:

  • 被模塊導(dǎo)出或開放的包(Package)
  • 類和接口的全限定名(Fully Qualified Name)
  • 字段的名稱和描述符(Descriptor)

常量池中每一項(xiàng)常量都是一個(gè)表,截至JDK 13,常量表中分別有17種不同類型的常量。17種常量類型所代表的具體含義如圖所示。

關(guān)于 class 文件常量池的更多內(nèi)容可以閱讀周志明的《深入理解Java虛擬機(jī)》6.3.2 章節(jié)。

4、運(yùn)行時(shí)常量池

class 文件常量池是在類被編譯成 class 文件時(shí)生成的。而當(dāng)類被加載到內(nèi)存中后,JVM 就會(huì)將 class 文件常量池中的內(nèi)容存放到運(yùn)行時(shí)常量池中。

Java 虛擬機(jī)規(guī)范中對(duì)運(yùn)行時(shí)常量池的定義如下:

A run-time constant pool is a per-class or per-interface run-time representation of the constant_pool table in a class file.

運(yùn)行時(shí)常量池是 class 文件中每一個(gè)類或接口的常量池表(constant_pool table)的運(yùn)行時(shí)表示形式。

因此,根據(jù)規(guī)范定義,可以說運(yùn)行時(shí)常量池是 class 文件常量池的運(yùn)行時(shí)表示,每個(gè)類在運(yùn)行時(shí)都有自己的一個(gè)獨(dú)立的運(yùn)行時(shí)常量池。

5、字符串常量池

簡(jiǎn)單來說,HotSpot VM 里的字符串常量池(StringTable)是個(gè)哈希表,全局只有一份,被所有的類共享。

StringTable 具體存儲(chǔ)的是 String 對(duì)象的引用,而不是 String 對(duì)象實(shí)例自身。String 對(duì)象實(shí)例在 JDK 6 及之前是在永久代里,從JDK 7 開始放在堆里。

根據(jù) Java 虛擬機(jī)規(guī)范的定義,堆是存儲(chǔ) Java 對(duì)象的地方,其他地方是不會(huì)有 Java 對(duì)象實(shí)體的,如果有的話,根據(jù)規(guī)范定義,這些地方也要算堆的一部分。

6、字符串常量池是否屬于方法區(qū)?

我認(rèn)為是不屬于的。

在讀本文之前,我相信很多同學(xué)會(huì)有如下觀點(diǎn):因?yàn)檫\(yùn)行時(shí)常量池屬于方法區(qū),所以很多同學(xué)認(rèn)為字符串常量池也應(yīng)該屬于方法區(qū)。

但是相信看了上面的內(nèi)容后,會(huì)開始意識(shí)到,運(yùn)行時(shí)常量池和字符串常量池其實(shí)是不同的兩個(gè)東西,當(dāng)然它們?cè)谧址馕鰰r(shí)會(huì)有關(guān)聯(lián)。

Java 虛擬機(jī)規(guī)范中對(duì)方法區(qū)的定義如下:

The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization

在 Java 虛擬機(jī)中,方法區(qū)是被各個(gè)線程共享的運(yùn)行時(shí)內(nèi)存區(qū)域。方法區(qū)類似于傳統(tǒng)語言的編譯代碼的存儲(chǔ)區(qū),或者類似于操作系統(tǒng)進(jìn)程中的文本段。它存儲(chǔ)了每一個(gè)類的結(jié)構(gòu)信息,例如:運(yùn)行時(shí)常量池、字段和方法數(shù)據(jù),構(gòu)造函數(shù)和普通方法的字節(jié)碼內(nèi)容,還包括一些用于類、實(shí)例、接口初始化用到的特殊方法。

這邊的關(guān)鍵在于 “它存儲(chǔ)了每一個(gè)類的結(jié)構(gòu)信息”,而字符串常量池并不屬于某個(gè)類,字符串常量是全局共享的,因此,根據(jù)規(guī)范定義,我們可以說字符串常量池不屬于方法區(qū)。

那字符串常量池(StringTable)究竟存在哪里了?

StringTable 本體是存儲(chǔ)在 native memory(本地內(nèi)存)里,不是在永久代里,不是在方法區(qū)里,當(dāng)然,更不是在堆里。

7、運(yùn)行時(shí)常量池和字符串常量池的關(guān)聯(lián)?

上面說了,運(yùn)行時(shí)常量池和字符串常量池在字符串解析時(shí)會(huì)有關(guān)聯(lián),具體如下。

類的運(yùn)行時(shí)常量池中有 CONSTANT_String_info(見題3表格)類型的常量,CONSTANT_String_info 類型的常量的解析(resolve)過程如下:

首先到字符串常量池(StringTable)中查找是否已經(jīng)有了該字符串的引用,如果有,則直接返回字符串常量池的引用;如果沒有,則在堆中創(chuàng)建 String 對(duì)象,并在字符串常量池駐留其引用,然后返回該引用。

也就說,運(yùn)行時(shí)常量池里的 CONSTANT_String_info 類型的常量,經(jīng)過解析(resolve)之后,同樣存的是字符串的引用,并且和 StringTable 駐留的引用的是一致的。

8、String#intern 方法

在 JDK 7 及之后的版本中,該方法的作用如下:如果字符串常量池中已經(jīng)有這個(gè)字符串,則直接返回常量池中的引用;如果沒有,則將這個(gè)字符串的引用保存一份到字符串常量池,然后返回這個(gè)引用。

下面的例子可以進(jìn)行簡(jiǎn)單的驗(yàn)證:

public static void main(String args[]) {

    // 創(chuàng)建2個(gè)對(duì)象,str持有的是new創(chuàng)建的對(duì)象引用
    // 1)駐留(intern)在字符串常量池中的對(duì)象
    // 2)new創(chuàng)建的對(duì)象
    String str = new String("joonwhee");
    // 字符串常量池中已經(jīng)有了,返回字符串常量池中的引用
    String str2 = "joonwhee";
    // false,str為new創(chuàng)建的對(duì)象引用,str2為字符創(chuàng)常量池中的引用
    System.out.println(str == str2);
    // str修改為字符串常量池的引用,所以下面為true
    str = str.intern();
    // true
    System.out.println(str == str2);
}

9、永久代(PermGen)

永久代在 Java 8 被移除。根據(jù)官方提案的描述,移除的主要?jiǎng)訖C(jī)是:要將 JRockit 和 Hotspot 進(jìn)行融合,而 JRockit 并沒有永久代。

而據(jù)我們所了解的,還有另外一個(gè)重要原因是永久代本身也存在較多的問題,經(jīng)常出現(xiàn)OOM,還出過不少bug。

根據(jù)官方提案的描述,永久代主要存儲(chǔ)了三種數(shù)據(jù):

1)Class metadata(類元數(shù)據(jù)),也就是方法區(qū)中包含的數(shù)據(jù),除了編譯生成的字節(jié)碼被放在 native memory(本地內(nèi)存)。

2)interned Strings,也就是字符串常量池中駐留引用的字符串對(duì)象,字符串常量池只駐留引用,而實(shí)際對(duì)象是在永久代中。

3)class static variables,類靜態(tài)變量。

移除永久代后,interned Strings 和 class static variables 被移動(dòng)了堆中,Class metadata 被移動(dòng)到了后來的元空間。

10、永久代和方法區(qū)的關(guān)系?

方法區(qū)是 Java 虛擬機(jī)規(guī)范中定義的一種邏輯概念,而永久代是對(duì)方法區(qū)的實(shí)現(xiàn)。但是永久代并不等同于方法區(qū),方法區(qū)也不等同于永久代。

永久代中的 interned Strings 并不屬于方法區(qū),按規(guī)范:堆是存儲(chǔ) Java 對(duì)象的地方 ,這部分應(yīng)該屬于堆,因此永久代并不是只用于實(shí)現(xiàn)方法區(qū)。

方法區(qū)中 JIT 編譯生成的代碼并不是存放在永久代,而是在 native memory 中,因此可以說方法區(qū)也并不只是由永久代來實(shí)現(xiàn)。

11、元空間(metaspace)

元空間在 Java 8 移除永久代后被引入,用來代替永久代,本質(zhì)和永久代類似,都是對(duì)方法區(qū)的實(shí)現(xiàn)。不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中,而是使用本地內(nèi)存(native memory)。

元空間主要用于存儲(chǔ) Class metadata(類元數(shù)據(jù)),根據(jù)其命名其實(shí)也看得出來。

可以通過 -XX:MaxMetaspaceSize 參數(shù)來限制元空間的大小,如果沒有設(shè)置該參數(shù),則元空間默認(rèn)限制為機(jī)器內(nèi)存。

12、為什么引入元空間?

在 Java 8 之前,Java 虛擬機(jī)使用永久代來存放類元信息,通過-XX:PermSize、-XX:MaxPermSize 來控制這塊內(nèi)存的大小,隨著動(dòng)態(tài)類加載的情況越來越多,這塊內(nèi)存變得不太可控,到底設(shè)置多大合適是每個(gè)開發(fā)者要考慮的問題。

如果設(shè)置小了,容易出現(xiàn)內(nèi)存溢出;如果設(shè)置大了,又有點(diǎn)浪費(fèi),盡管不會(huì)實(shí)質(zhì)分配這么大的物理內(nèi)存。

而元空間可以較好的解決內(nèi)存設(shè)置多大的問題:當(dāng)我們沒有指定 -XX:MaxMetaspaceSize 時(shí),元空間可以動(dòng)態(tài)的調(diào)整使用的內(nèi)存大小,以容納不斷增加的類。

13、元空間能徹底解決內(nèi)存溢出(Out Of Memory)問題嗎?

很遺憾,答案是不行的。

元空間無法徹底解決內(nèi)存溢出的問題,只能說是有所緩解。當(dāng)內(nèi)存使用完畢后,元空間一樣會(huì)出現(xiàn)內(nèi)存溢出的情況,最典型的場(chǎng)景就是出現(xiàn)了內(nèi)存泄漏時(shí)。

總結(jié)

本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • 淺談Java中各種修飾符與訪問修飾符的說明

    淺談Java中各種修飾符與訪問修飾符的說明

    下面小編就為大家?guī)硪黄獪\談Java中各種修飾符與訪問修飾符的說明。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-01-01
  • Java中List.contains(Object?object)方法使用

    Java中List.contains(Object?object)方法使用

    本文主要介紹了Java中List.contains(Object?object)方法,使用List.contains(Object?object)方法判斷ArrayList是否包含一個(gè)元素對(duì)象,感興趣的可以了解一下
    2022-04-04
  • 使用spring的restTemplate注意點(diǎn)

    使用spring的restTemplate注意點(diǎn)

    這篇文章主要介紹了使用spring的restTemplate注意點(diǎn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 快速排序算法原理及java遞歸實(shí)現(xiàn)

    快速排序算法原理及java遞歸實(shí)現(xiàn)

    快速排序 對(duì)冒泡排序的一種改進(jìn),若初始記錄序列按關(guān)鍵字有序或基本有序,蛻化為冒泡排序。使用的是遞歸原理,在所有同數(shù)量級(jí)O(n longn) 的排序方法中,其平均性能最好。就平均時(shí)間而言,是目前被認(rèn)為最好的一種內(nèi)部排序方法
    2014-01-01
  • 理解java多線程中ExecutorService使用

    理解java多線程中ExecutorService使用

    這篇文章主要幫助大家理解java多線程中ExcetorServiced的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Maven的配置文件pom.xml詳解(含常用plugin)

    Maven的配置文件pom.xml詳解(含常用plugin)

    pom.xml是Maven項(xiàng)目的核心配置文件,它是 項(xiàng)目對(duì)象模型 - Project Object Model(POM)的縮寫,本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來管理項(xiàng)目,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • 使用Spring Boot創(chuàng)建Web應(yīng)用程序的示例代碼

    使用Spring Boot創(chuàng)建Web應(yīng)用程序的示例代碼

    本篇文章主要介紹了使用Spring Boot創(chuàng)建Web應(yīng)用程序的示例代碼,我們將使用Spring Boot構(gòu)建一個(gè)簡(jiǎn)單的Web應(yīng)用程序,并為其添加一些有用的服務(wù),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • java ExecutorService使用方法詳解

    java ExecutorService使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了java ExecutorService使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Java多線程之同步工具類Exchanger

    Java多線程之同步工具類Exchanger

    這篇文章主要介紹了Java多線程之同步工具類Exchanger,Exchanger 是一個(gè)用于線程間協(xié)作的工具類,Exchanger用于進(jìn)行線程間的數(shù)據(jù)交換,它提供一個(gè)同步點(diǎn),在這個(gè)同步點(diǎn),兩個(gè)線程可以交換彼此的數(shù)據(jù),下面來看看具體內(nèi)容吧
    2021-10-10
  • Java中StringBuilder常用構(gòu)造方法解析

    Java中StringBuilder常用構(gòu)造方法解析

    這篇文章主要介紹了Java中StringBuilder常用構(gòu)造方法解析,StringBuilder是一個(gè)可標(biāo)的字符串類,我們可以吧它看成是一個(gè)容器這里的可變指的是StringBuilder對(duì)象中的內(nèi)容是可變的,需要的朋友可以參考下
    2024-01-01

最新評(píng)論