JVM虛擬機學習(1)---Jvm內存區域

目錄頁:https://mp.csdn.net/postedit/95937156

1.小聲嗶嗶

    本文主要基於JDK1.8,網上已有許多大神對JVM內存區域劃分做了詳解,本文目的主要是對自己學習心得的記錄,如有錯誤請指正。

2.運行時數據區

    JVM在運行過程中會把它所管理的內存劃分爲若干不同數據區域

  1. 線程私有:程序計數器、本地方法棧、虛擬機棧
  2. 線程共享:堆、方法區

如下圖所示:

 

2.1. 程序計數器

    程序計數器指向正在執行的字節碼指令的地址(行號),爲什麼需要程序計數器呢,因爲Java是多線程的,程序計數器就是保證多線程情況下線程切換後程序仍能正常執行。程序計數器是唯一不會OOM的區域

2.2. 虛擬機棧

    虛擬機棧是線程私有的,生命週期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行同時會創建一個棧幀,棧幀中存儲局部變量表、操作數棧、動態鏈接,方法出口等信息。每一個方法從調用到執行完成的過程都對應着一個棧幀在虛擬機棧中入棧到出棧的過程。

    局部變量表中存放了基本數據類型(boolean,byte,char,short,int,float,long,double)和對象引用,局部變量所需內存空間在編譯期完成分配,long和double會佔用兩個局部變量空間,其餘數據類型佔用一個。

    若單個線程請求的棧深度大於虛擬機允許的深度,則會拋出StackOverflowError(棧溢出錯誤)。當整個虛擬機棧內存耗盡會拋出OutOfMemoryError異常。

    局部變量表:記錄局部變量

    操作數棧:用於計算的臨時數據存儲區,大多數指令都在操作數棧彈棧運算,然後結果壓棧。

    動態連接:多態機制時運行期根據實際類型確定執行哪個方法。

    返回地址:方法返回

2.3. 本地方法棧

    本地方法棧的功能和特點類似於虛擬機棧,均具有線程隔離的特點以及都能拋出StackOverflowError和OutOfMemoryError異常。不同的是,本地方法棧服務的對象是JVM執行的native方法(例如hashCode方法),當一個JVM創建的線程調用native方法後,JVM不再爲其在虛擬機中創建棧幀,只是簡單的動態鏈接並直接調用native方法。而虛擬機棧服務的是JVM執行的java方法。我們常用的HotSpot虛擬機選擇合併了虛擬機棧和本地方法棧。

2.4. Java堆

    Java被所有線程共享,虛擬機啓動時創建,需要關注的三個啓動參數如下:-Xms:初始大小,-Xmx:最大大小,-Xmn:新生代大小。Java堆用於存放對象實例,是垃圾收集器的主要區域。

2.5. Java方法區(永久代、元空間)

    與Java堆一樣,是各個線程共享的內存區域,用於存儲虛擬機加載的類信息、常量(被final關鍵字修飾)、靜態變量(被static關鍵字修飾)、即時編譯期編譯後的代碼等數據。更加詳細一點的說法是方法區裏存放着類的版本,字段,方法,接口和常量池。常量池裏存儲着字面量和符號引用。

    JDK1.8版本之前有人會誤以爲永久代與方法區是等價的,實際情況是使用永久代實現方法區這個概念,可以將方法區理解爲接口,永久代爲具體的實現方法。因爲JDK1.8以後做了去永久代,就是使用元空間來實現方法區,同時隔離了堆內存的垃圾回收與元空間的垃圾回收。元空間大小隻受制於機器內存大小。

    永久代啓動參數:-XX:PermSize(初始值)   -XX:MaxPermSize(最大值)

    元空間啓動參數:-XX:MetapaceSize(初始值) -XXMaxMetaspaceSize(最大值)

2.5.1. 常量池:全局字符串池

    全局字符串池裏的內容是在類加載完成,經過驗證,準備階段之後在堆中生成字符串對象實例,然後將該字符串對象實例的引用值存到string pool中(記住:string pool中存的是引用值而不是具體的實例對象,具體的實例對象是在堆中開闢的一塊空間存放的)。 在HotSpot VM裏實現的string pool功能的是一個StringTable類,它是一個哈希表,裏面存的是駐留字符串(也就是我們常說的用雙引號括起來的)的引用(而不是駐留字符串實例本身),也就是說在堆中的某些字符串實例被這個StringTable引用之後就等同被賦予了”駐留字符串”的身份。這個StringTable在每個HotSpot VM的實例只有一份,被所有的類共享。

2.5.2. 常量池:class文件常量池

    當java文件被編譯成class文件之後會生成class文件常量池,我們都知道,class文件中除了包含類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池(constant pool table),用於存放編譯器生成的各種字面量(Literal)和符號引用(Symbolic References)。 字面量就是我們所說的常量概念,如文本字符串、被聲明爲final的常量值等。 符號引用是一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。一般包括下面三類常量:

  1. 類和接口的全限定名
  2. 字段的名稱和描述符
  3. 方法的名稱和描述符

2.5.3. 常量池:運行時常量池

    jvm在執行某個類的時候,必須經過加載、連接、初始化,而連接又包括驗證、準備、解析三個階段。而當類加載到內存中後,jvm就會將class常量池中的內容存放到運行時常量池中,由此可知,運行時常量池也是每個類都有一個。在上面我也說了,class常量池中存的是字面量和符號引用,也就是說他們存的並不是對象的實例,而是對象的符號引用值。而經過解析(resolve)之後,也就是把符號引用替換爲直接引用,直接引用一般是指向方法區的本地指針,解析的過程會去查詢全局字符串池,也就是我們上面所說的StringTable,以保證運行時常量池所引用的字符串與全局字符串池中所引用的是一致的。

    JDK1.6運行時常量池放在方法區中,JDK1.7運行時常量池放在堆中。

    以上2.5.1,2.5.2,2.5.3參照博客:http://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool/

2.6. 直接內存

直接內存不是虛擬機運行時的數據區一部分,在使用NIO的時候這個區域會被頻繁調用,在Java堆內可以使用directByteBuffer對象直接引用並操作,這塊內存受本機內存大小限制,可以通過MaxDirectMemorySize來設置(默認與堆內存大小一致),也會出現OOM內存異常。使用NIO可以避免在JAVA堆和Native堆中來回複製數據,能夠提高效率。

2.7. 思維導圖

 

參考資料:周志明大神-《深入理解Java虛擬機 JVM高級特性與最佳實踐》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章