方法區與Java堆一樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。
使用HotSpot虛擬機的用戶,更願意把方法區稱為“永久代”,本質上兩者並不等價,僅僅是因為HotSpot虛擬機的設計團隊選擇把GC分代收集至方法區,或者說用永久代來實現方法區而已。這樣HotSpot的垃圾收集器可以像管理Java堆一樣管理這部分內存,能省去專門為方法區編寫內存管理代碼的工作。
移除永久代的工作從JDK1.7就開始了。JDK1.7中,存儲在永久代的部分數據就已經轉移到了Java Heap或者是 Native Heap。但永久代仍存在於JDK1.7中,並沒完全移除,譬如符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap;類的靜態變量(class statics)轉移到了java heap。
元空間本質和永久代類似,都是對JVM規范中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用本地內存。因此默認情況下元空間的大小僅受本地內存限制。
即方法區里存放着類的版本、字段、方法、接口和常量池(存儲字面量和符號引用)。
符號引用包括:1、類的權限定名;2、字段名和屬性;3、方法名和屬性。
1、類型信息:
類的完整名稱
類的直接父類的完整名稱
類的直接實現接口的有序列表
類型標志(類類型還是接口類型)
類的修飾符(public private defautl abstract final static)
2、類型的常量池
存放該類型所用到的常量的有序集合,包括直接常量(字符串、整數、浮點數)和對其他類型、字段、方法的符號引用。
3、字段信息(該類聲明的所有字段)
字段修飾符(public、peotect、private、default)
字段的類型
字段名稱
4、方法信息
方法信息中包含類的所有方法。
方法修飾符
方法返回類型
方法名
方法參數個數、類型、順序等
方法字節碼
操作數棧和該方法在棧幀中的局部變量區大小
異常表
5、類變量(靜態變量)
6、指向類加載器的引用
7、指向Class實例的引用
8、方法表
9、運行時常量池(Runtime Constant Pool)
總結:
HotSpot JVM中,永久代中用於存放類和方法的元數據以及常量池。每當一個類初次被加載的時候,它的元數據都會被放到永久代中。
永久代大小有限制,如果加載的類太多,很可能導致永久代內存溢出,即java.lang.OutOfMemoryError: PermGen。
Java 8中PermGen被移出HotSpot JVM了:
- 由於 PermGen 內存經常會溢出,引發惱人的 java.lang.OutOfMemoryError: PermGen,因此 JVM 的開發者希望這一塊內存可以更靈活地被管理,不要再經常出現這樣的 OOM
- 移除 PermGen 可以促進 HotSpot JVM 與 JRockit VM 的融合,因為 JRockit 沒有永久代。
PermGen最終被移出,方法區移至Metaspace,字符串常量移至Java Heap。
JDK8把類的元數據放到本地堆內存(native heap)中,這一塊區域就叫Metaspace,中文名叫元空間。
如果Metaspace的空間占用達到了設定的最大值,那么就會觸發GC來收集死亡對象和類的加載器。