偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

開發(fā) 前端
jdk7?中將StringTable?放到了堆空間中。因為永久代的回收效率很低,在full gc?的時候才會觸發(fā)。而full gc是老年代的空間不足、永久代不足時才會觸發(fā)。

前言

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

圖片圖片

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

當我們通過前面的:類的加載-> 驗證 -> 準備 -> 解析 -> 初始化 這幾個階段完成后,就會用到執(zhí)行引擎對我們的類進行使用,同時執(zhí)行引擎將會使用到我們運行時數(shù)據(jù)區(qū)。

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

正文

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

圖片圖片

線程

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

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

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

JVM系統(tǒng)線程:

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

方法區(qū)

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

圖片圖片

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

方法區(qū)基本理解

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

方法區(qū)的演進

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

圖片圖片

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

圖片圖片

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

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

jdk7及以前:

  • 通過-XX:Permsize來設置永久代初始分配空間。默認值是20.75M
  • 通過-XX:MaxPermsize來設定永久代最大可分配空間。32位機器默認是64M,64位機器模式是82M

圖片圖片

jdk8及以后:

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

方法區(qū)的內(nèi)部結構

圖片圖片

方法區(qū)存儲什么

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

圖片圖片

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

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

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

  • 域的相關信息包括:域名稱、域類型、域修飾符(public,private,protected,static,final,volatile,transient的某個子集)

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

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

每個異常處理的開始位置、結束位置、代碼處理在程序計數(shù)器中的偏移地址、被捕獲的異常類的常量池索引

類變量:

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

全局常量:

  • 被聲明為final的類變量的處理方法則不同,每個全局常量在編譯的時候就會被分配了

常量池

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

圖片圖片

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

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

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

運行時常量池

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

圖片圖片

詳細執(zhí)行過程

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

圖片圖片

方法區(qū)的演進細節(jié)

jdk1.6:

圖片圖片

jdk1.7:

圖片圖片

jdk1.8:

圖片圖片

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

jdk7中將StringTable放到了堆空間中。因為永久代的回收效率很低,在full gc的時候才會觸發(fā)。而full gc是老年代的空間不足、永久代不足時才會觸發(fā)。

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

責任編輯:武曉燕 來源: 一安未來
相關推薦

2024-03-29 11:42:21

Java虛擬機

2012-11-14 09:57:46

JavaJava虛擬機JVM

2019-07-24 16:04:47

Java虛擬機并發(fā)

2024-03-26 07:30:07

Java虛擬機源文件

2016-09-01 12:37:13

OpenStack虛擬機Metadata

2024-04-10 07:40:45

Java虛擬機內(nèi)存

2023-09-22 23:00:11

Java虛擬機

2019-12-31 10:45:30

JavaVisualVM高并發(fā)

2017-11-14 14:41:11

Java泛型IO

2013-11-05 13:29:04

JavaScriptreplace

2011-12-28 13:24:47

JavaJVM

2011-12-28 13:38:00

JavaJVM

2010-06-01 15:25:27

JavaCLASSPATH

2016-12-08 15:36:59

HashMap數(shù)據(jù)結構hash函數(shù)

2020-07-21 08:26:08

SpringSecurity過濾器

2020-05-08 16:55:48

Java虛擬機JVM

2009-12-16 13:44:12

2022-08-21 16:52:27

Linux虛擬內(nèi)存

2021-09-18 06:56:01

JavaCAS機制

2012-03-05 11:09:01

JavaClass
點贊
收藏

51CTO技術棧公眾號