自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入理解Java虛擬機(jī):方法區(qū)詳解

開(kāi)發(fā) 前端
jdk7?中將StringTable?放到了堆空間中。因?yàn)橛谰么幕厥招屎艿停趂ull gc?的時(shí)候才會(huì)觸發(fā)。而full gc是老年代的空間不足、永久代不足時(shí)才會(huì)觸發(fā)。

前言

本節(jié)主要講的是運(yùn)行時(shí)數(shù)據(jù)區(qū)(方法區(qū)),也就是下圖這部分,它是在類(lèi)加載完成后的階段:

圖片圖片

  • 每個(gè)線(xiàn)程:獨(dú)立包括程序計(jì)數(shù)器、棧、本地棧
  • 線(xiàn)程間共享:堆、堆外內(nèi)存(永久代或元空間、代碼緩存)

當(dāng)我們通過(guò)前面的:類(lèi)的加載-> 驗(yàn)證 -> 準(zhǔn)備 -> 解析 -> 初始化 這幾個(gè)階段完成后,就會(huì)用到執(zhí)行引擎對(duì)我們的類(lèi)進(jìn)行使用,同時(shí)執(zhí)行引擎將會(huì)使用到我們運(yùn)行時(shí)數(shù)據(jù)區(qū)。

內(nèi)存是非常重要的系統(tǒng)資源,是硬盤(pán)和CPU的中間倉(cāng)庫(kù)及橋梁,承載著操作系統(tǒng)和應(yīng)用程序的實(shí)時(shí)運(yùn)行JVM內(nèi)存布局規(guī)定了Java在運(yùn)行過(guò)程中內(nèi)存申請(qǐng)、分配、管理的策略,保證了JVM的高效穩(wěn)定運(yùn)行。不同的JVM對(duì)于內(nèi)存的劃分方式和管理機(jī)制存在著部分差異。

正文

我們通過(guò)磁盤(pán)或者網(wǎng)絡(luò)IO得到的數(shù)據(jù),都需要先加載到內(nèi)存中,然后CPU從內(nèi)存中獲取數(shù)據(jù)進(jìn)行讀取,也就是說(shuō)內(nèi)存充當(dāng)了CPU和磁盤(pán)之間的橋梁。

圖片圖片

線(xiàn)程

線(xiàn)程是一個(gè)程序里的運(yùn)行單元。JVM允許一個(gè)應(yīng)用有多個(gè)線(xiàn)程并行的執(zhí)行。在Hotspot JVM里,每個(gè)線(xiàn)程都與操作系統(tǒng)的本地線(xiàn)程直接映射。

當(dāng)一個(gè)Java線(xiàn)程準(zhǔn)備好執(zhí)行以后,此時(shí)一個(gè)操作系統(tǒng)的本地線(xiàn)程也同時(shí)創(chuàng)建。Java線(xiàn)程執(zhí)行終止后,本地線(xiàn)程也會(huì)回收。

操作系統(tǒng)負(fù)責(zé)所有線(xiàn)程的安排調(diào)度到任何一個(gè)可用的CPU上。一旦本地線(xiàn)程初始化成功,它就會(huì)調(diào)用Java線(xiàn)程中的run()方法。

JVM系統(tǒng)線(xiàn)程:

  • 虛擬機(jī)線(xiàn)程:需要JVM達(dá)到安全點(diǎn)才會(huì)出現(xiàn)。這些操作必須在不同的線(xiàn)程中發(fā)生的,原因是他們都需要JVM達(dá)到安全點(diǎn),這樣堆才不會(huì)變化。這種線(xiàn)程的執(zhí)行類(lèi)型包括stop-the-world的垃圾收集,線(xiàn)程棧收集,線(xiàn)程掛起以及偏向鎖撤銷(xiāo)。
  • 周期任務(wù)線(xiàn)程:這種線(xiàn)程是時(shí)間周期事件的體現(xiàn)(比如中斷),他們一般用于周期性操作的調(diào)度執(zhí)行。
  • GC線(xiàn)程:這種線(xiàn)程對(duì)在JVM里不同種類(lèi)的垃圾收集行為提供了支持。
  • 編譯線(xiàn)程:這種線(xiàn)程在運(yùn)行時(shí)會(huì)將字節(jié)碼編譯成到本地代碼。
  • 信號(hào)調(diào)度線(xiàn)程:這種線(xiàn)程接收信號(hào)并發(fā)送給JVM,在它內(nèi)部通過(guò)調(diào)用適當(dāng)?shù)姆椒ㄟM(jìn)行處理。

方法區(qū)

棧、堆、方法區(qū)的交互關(guān)系

圖片圖片

盡管所有的方法區(qū)在邏輯上是屬于堆的一部分,但一些簡(jiǎn)單的實(shí)現(xiàn)可能不會(huì)選擇去進(jìn)行垃圾收集或者進(jìn)行壓縮。但對(duì)于HotSpotJVM而言,方法區(qū)還有一個(gè)別名叫做Non-Heap(非堆),目的就是要和堆分開(kāi),所以方法區(qū)看作是一塊獨(dú)立于Java堆的內(nèi)存空間。

方法區(qū)基本理解

  • 方法區(qū)(Method Area)與Java堆一樣,是各個(gè)線(xiàn)程共享的內(nèi)存區(qū)域。
  • 方法區(qū)在JVM啟動(dòng)的時(shí)候被創(chuàng)建,并且它的實(shí)際的物理內(nèi)存空間中和Java堆區(qū)一樣都可以是不連續(xù)的。
  • 方法區(qū)的大小,跟堆空間一樣,可以選擇固定大小或者可擴(kuò)展。
  • 方法區(qū)的大小決定了系統(tǒng)可以保存多少個(gè)類(lèi),如果系統(tǒng)定義了太多的類(lèi),導(dǎo)致方法區(qū)溢出,虛擬機(jī)同樣會(huì)拋出內(nèi)存溢出錯(cuò)誤:java.lang.OutOfMemoryError: PermGen space 或者java.lang.OutOfMemoryError: Metaspace
  • 加載大量的第三方的jar包;Tomcat部署的工程過(guò)多(30~50個(gè));大量動(dòng)態(tài)的生成反射類(lèi)
  • 關(guān)閉JVM就會(huì)釋放這個(gè)區(qū)域的內(nèi)存。

方法區(qū)的演進(jìn)

在jdk7及以前,習(xí)慣上把方法區(qū),稱(chēng)為永久代。jdk8開(kāi)始,使用元空間取代了永久代

圖片圖片

JDK8完全廢棄了永久代的概念,改用與JRockit、J9一樣在本地內(nèi)存中實(shí)現(xiàn)的元空間(Metaspace)來(lái)代替

圖片圖片

元空間的本質(zhì)和永久代類(lèi)似,都是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)。不過(guò)元空間與永久代最大的區(qū)別在于:元空間不在虛擬機(jī)設(shè)置的內(nèi)存中,而是使用本地內(nèi)存。

設(shè)置方法區(qū)內(nèi)存的大小

jdk7及以前:

  • 通過(guò)-XX:Permsize來(lái)設(shè)置永久代初始分配空間。默認(rèn)值是20.75M
  • 通過(guò)-XX:MaxPermsize來(lái)設(shè)定永久代最大可分配空間。32位機(jī)器默認(rèn)是64M,64位機(jī)器模式是82M

圖片圖片

jdk8及以后:

  • 元數(shù)據(jù)區(qū)大小可以使用參數(shù) -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize指定
  • -XX:MetaspaceSize設(shè)置初始的元空間大小。對(duì)于一個(gè)64位的服務(wù)器端JVM來(lái)說(shuō),其默認(rèn)的-XX:MetaspaceSize值為21MB,這就是初始的高水位線(xiàn),一旦觸及這個(gè)水位線(xiàn),F(xiàn)ull GC將會(huì)被觸發(fā)并卸載沒(méi)用的類(lèi)(即這些類(lèi)對(duì)應(yīng)的類(lèi)加載器不再存活),然后這個(gè)高水位線(xiàn)將會(huì)重置。新的高水位線(xiàn)的值取決于GC后釋放了多少元空間。如果釋放的空間不足,那么在不超過(guò)MaxMetaspaceSize時(shí),適當(dāng)提高該值。如果釋放空間過(guò)多,則適當(dāng)降低該值。

方法區(qū)的內(nèi)部結(jié)構(gòu)

圖片圖片

方法區(qū)存儲(chǔ)什么

它用于存儲(chǔ)已被虛擬機(jī)加載的類(lèi)型信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼緩存等

圖片圖片

類(lèi)型信息,對(duì)每個(gè)加載的類(lèi)型(類(lèi)class、接口interface、枚舉enum、注解annotation),JVM必須在方法區(qū)中存儲(chǔ)以下類(lèi)型信息:

  • 這個(gè)類(lèi)型的完整有效名稱(chēng)(全名=包名.類(lèi)名)
  • 這個(gè)類(lèi)型直接父類(lèi)的完整有效名(對(duì)于interface或java.lang.Object,都沒(méi)有父類(lèi))
  • 這個(gè)類(lèi)型的修飾符(public,abstract,final的某個(gè)子集)
  • 這個(gè)類(lèi)型直接接口的一個(gè)有序列表

域信息,JVM必須在方法區(qū)中保存類(lèi)型的所有域的相關(guān)信息以及域的聲明順序:

  • 域的相關(guān)信息包括:域名稱(chēng)、域類(lèi)型、域修飾符(public,private,protected,static,final,volatile,transient的某個(gè)子集)

方法信息,JVM必須保存所有方法的以下信息,同域信息一樣包括聲明順序:

  • 方法名稱(chēng)
  • 方法的返回類(lèi)型(或void)
  • 方法參數(shù)的數(shù)量和類(lèi)型(按順序)
  • 方法的修飾符(public,private,protected,static,final,synchronized,native,abstract的一個(gè)子集)
  • 方法的字節(jié)碼(bytecodes)、操作數(shù)棧、局部變量表及大小(abstract和native方法除外)
  • 異常表(abstract和native方法除外)

每個(gè)異常處理的開(kāi)始位置、結(jié)束位置、代碼處理在程序計(jì)數(shù)器中的偏移地址、被捕獲的異常類(lèi)的常量池索引

類(lèi)變量:

  • 靜態(tài)變量和類(lèi)關(guān)聯(lián)在一起,隨著類(lèi)的加載而加載,他們成為類(lèi)數(shù)據(jù)在邏輯上的一部分
  • 類(lèi)變量被類(lèi)的所有實(shí)例共享,即使沒(méi)有類(lèi)實(shí)例時(shí),你也可以訪(fǎng)問(wèn)它

全局常量:

  • 被聲明為final的類(lèi)變量的處理方法則不同,每個(gè)全局常量在編譯的時(shí)候就會(huì)被分配了

常量池

  • 字節(jié)碼文件,內(nèi)部包含了常量池(數(shù)量值、字符串值、類(lèi)引用、字段引用、方法引用)

圖片圖片

一個(gè)有效的字節(jié)碼文件中除了包含類(lèi)的版本信息、字段、方法以及接口等描述符信息外,還包含一項(xiàng)信息就是常量池表(Constant Pool Table),包括各種字面量和對(duì)類(lèi)型、域和方法的符號(hào)引用。

一個(gè)Java源文件中的類(lèi)、接口,編譯后產(chǎn)生一個(gè)字節(jié)碼文件。而Java中的字節(jié)碼需要數(shù)據(jù)支持,通常這種數(shù)據(jù)會(huì)很大以至于不能直接存到字節(jié)碼里,換另一種方式,可以存到常量池,這個(gè)字節(jié)碼包含了指向常量池的引用,在動(dòng)態(tài)鏈接的時(shí)候會(huì)用到運(yùn)行時(shí)常量池。

常量池可以看做是一張表,虛擬機(jī)指令根據(jù)這張常量表找到要執(zhí)行的類(lèi)名、方法名、參數(shù)類(lèi)型、字面量等類(lèi)型。

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

  • 運(yùn)行時(shí)常量池是方法區(qū)的一部分。
  • 常量池表是Class文件的一部分,用于存放編譯期生成的各種字面量與符號(hào)引用,這部分內(nèi)容將在類(lèi)加載后存放到方法區(qū)的運(yùn)行時(shí)常量池中。
  • 運(yùn)行時(shí)常量池,在加載類(lèi)和接口到虛擬機(jī)后,就會(huì)創(chuàng)建對(duì)應(yīng)的運(yùn)行時(shí)常量池。
  • JVM為每個(gè)已加載的類(lèi)型(類(lèi)或接口)都維護(hù)一個(gè)常量池。池中的數(shù)據(jù)項(xiàng)像數(shù)組項(xiàng)一樣,是通過(guò)索引訪(fǎng)問(wèn)的。
  • 運(yùn)行時(shí)常量池中包含多種不同的常量,包括編譯期就已經(jīng)明確的數(shù)值字面量,也包括到運(yùn)行期解析后才能夠獲得的方法或者字段引用。此時(shí)不再是常量池中的符號(hào)地址了,這里換為真實(shí)地址。
  • 運(yùn)行時(shí)常量池,相對(duì)于Class文件常量池的另一重要特征是:具備動(dòng)態(tài)性。
  • 運(yùn)行時(shí)常量池類(lèi)似于傳統(tǒng)編程語(yǔ)言中的符號(hào)表(symboltable),但是它所包含的數(shù)據(jù)卻比符號(hào)表要更加豐富一些。
  • 當(dāng)創(chuàng)建類(lèi)或接口的運(yùn)行時(shí)常量池時(shí),如果構(gòu)造運(yùn)行時(shí)常量池所需的內(nèi)存空間超過(guò)了方法區(qū)所能提供的最大值,則JVM會(huì)拋OutOfMemoryError異常。

方法區(qū)使用舉例

public class MethodAreaDemo {
    public static void main(String args[]) {
        int x = 500;
        int y = 100;
        int a = x / y;
        int b = 50;
        System.out.println(a+b);
    }
}

圖片圖片

詳細(xì)執(zhí)行過(guò)程

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

方法區(qū)的演進(jìn)細(xì)節(jié)

jdk1.6:

圖片圖片

jdk1.7:

圖片圖片

jdk1.8:

圖片圖片

StringTable為什么要調(diào)整位置

jdk7中將StringTable放到了堆空間中。因?yàn)橛谰么幕厥招屎艿?,在full gc的時(shí)候才會(huì)觸發(fā)。而full gc是老年代的空間不足、永久代不足時(shí)才會(huì)觸發(fā)。

這就導(dǎo)致StringTable回收效率不高。而我們開(kāi)發(fā)中會(huì)有大量的字符串被創(chuàng)建,回收效率低,導(dǎo)致永久代內(nèi)存不足。放到堆里,能及時(shí)回收內(nèi)存。

責(zé)任編輯:武曉燕 來(lái)源: 一安未來(lái)
相關(guān)推薦

2024-03-29 11:42:21

Java虛擬機(jī)

2012-11-14 09:57:46

JavaJava虛擬機(jī)JVM

2019-07-24 16:04:47

Java虛擬機(jī)并發(fā)

2024-03-26 07:30:07

Java虛擬機(jī)源文件

2016-09-01 12:37:13

OpenStack虛擬機(jī)Metadata

2024-04-10 07:40:45

Java虛擬機(jī)內(nèi)存

2023-09-22 23:00:11

Java虛擬機(jī)

2019-12-31 10:45:30

JavaVisualVM高并發(fā)

2017-11-14 14:41:11

Java泛型IO

2011-12-28 13:24:47

JavaJVM

2020-05-08 16:55:48

Java虛擬機(jī)JVM

2013-11-05 13:29:04

JavaScriptreplace

2011-12-28 13:38:00

JavaJVM

2009-12-16 13:44:12

2010-06-01 15:25:27

JavaCLASSPATH

2016-12-08 15:36:59

HashMap數(shù)據(jù)結(jié)構(gòu)hash函數(shù)

2020-07-21 08:26:08

SpringSecurity過(guò)濾器

2012-03-05 11:09:01

JavaClass

2022-08-21 16:52:27

Linux虛擬內(nèi)存

2021-09-18 06:56:01

JavaCAS機(jī)制
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)