1、方法區-Method Area
線程共享,存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等等。(HotSpot虛擬機上開發部署人員更願意成爲“永久代”,Permanent Generation)。
1.1、類型信息
- 類型的全限定名
- 超類的全限定名
- 直接超接口的全限定名
- 類型標誌(該類是類類型還是接口類型)
- 類的訪問描述符(public、private、default、abstract、final、static)
1.2、類型的常量池
(該部分是獨有的,然後運行時,把該部分加載進運行時常量池,當調用時則從符號引用解析爲直接引用,但是有些確定的方法會直接轉換,比如靜態方法,比如構造方法)
存放該類型所用到的常量的有序集合,包括直接常量(如字符串、整數、浮點數的常量)和對其他類型、字段、方法的符號引用。常量池中每一個保存的常量都有一個索引,就像數組中的字段一樣。因爲常量池中保存中所有類型使用到的類型、字段、方法的符號引用,所以它也是動態連接(棧中對應的方法指向這個引用)的主要對象(在動態鏈接中起到核心作用)。
1.3、字段信息(該類聲明的所有字段)
- 字段修飾符(public、protect、private、default)
- 字段的類型
- 字段名稱
1.4、方法信息
方法信息中包含類的所有方法,每個方法包含以下信息:
- 方法名
- 方法的返回類型(包括void)
- 方法參數的類型、數目以及順序
- 方法修飾符(public,private,protected,static,final,synchronized,native,abstract)
針對非本地方法,還有些附加方法信息需要存儲在方法區內:
- 方法字節碼
- 方法中局部變量區的大小、方法棧幀
- 異常表
1.5、類變量(靜態變量)
指該類所有對象共享的變量,即使沒有任何實例對象時,也可以訪問的類變量。它們與類進行綁定。
1.6、指向類加載器的引用
每一個被JVM加載的類型,都保存這個類加載器的引用,類加載器動態鏈接時會用到。
1.7、指向Class實例的引用
類加載的過程中,虛擬機會創建該類型的Class實例,方法區中必須保存對該對象的引用。通過Class.forName(StringclassName)來查找獲得該實例的引用,然後創建該類的對象。
1.8、方法表
爲了提高訪問效率,JVM可能會對每個裝載的非抽象類,都創建一個數組,數組的每個元素是實例可能調用的方法的直接引用,包括父類中繼承過來的方法。這個表在抽象類或者接口中是沒有的,類似C++虛函數表vtbl。
2、堆-Heap
JVM內存底層結構:
- VM內存劃分爲堆內存和非堆內存,堆內存分爲年輕代(Young Generation)、老年代(Old Generation),非堆內存就一個永久代(Permanent Generation)。
- 年輕代又分爲Eden和Survivor區。Survivor區由FromSpace和ToSpace組成。Eden區佔大容量,Survivor兩個區佔小容量,默認比例是8:1:1。
堆內存用途:存放的是對象,垃圾收集器就是收集這些對象,然後根據GC算法回收。
非堆內存用途:永久代,也稱爲方法區,存儲程序運行時長期存活的對象,比如類的元數據、方法、常量、屬性等。
2.1、JDK1.8之前
一個對象被創建以後首先被放到的Eden內存中,如果存活期超兩個Survivor之後就會被轉移到長時內存中(Old Generation)中。永久內存中存放着對象的方法、變量等元數據信息。如果永久內存不夠,會發生錯誤:java.lang.OutOfMemoryError: PermGen
2.1、JDK1.8之後
把存放元數據中的永久內存從堆內存中移到了本地內存(native memory)中。
永久內存就不再佔用堆內存,它可以通過自動增長來避免JDK7以及前期版本中常見的永久內存錯誤(java.lang.OutOfMemoryError: PermGen)。當然JDK8也提供了一個新的設置Matespace內存大小的參數,通過這個參數可以設置Matespace內存大小,這樣我們可以根據自己項目的實際情況,避免過度浪費本地內存,達到有效利用。