1. 程式人生 > >JAVA 方法區與堆--java7前,java7,java8各不相同

JAVA 方法區與堆--java7前,java7,java8各不相同

三種情況:
java7之前,方法區位於永久代(PermGen),永久代和堆相互隔離,永久代的大小在啟動JVM時可以設定一個固定值,不可變;
java7中,儲存在永久代的部分資料就已經轉移到Java Heap或者Native memory。但永久代仍存在於JDK 1.7中,並沒有完全移除,譬如符號引用(Symbols)轉移到了native memory;字串常量池(interned strings)轉移到了Java heap;類的靜態變數(class statics)轉移到了Java heap。
java8中,取消永久代,方法存放於元空間(Metaspace),元空間仍然與堆不相連,但與堆共享實體記憶體,邏輯上可認為在堆中
Native memory:本地記憶體,也稱為C-Heap,是供JVM自身程序使用的。當Java Heap空間不足時會觸發GC,但Native memory空間不夠卻不會觸發GC。


知乎上:R大神解答:
Oracle JDK7 / OpenJDK 7的HotSpot VM是把Symbol的儲存從PermGen移動到了native memory,並且把靜態變數從instanceKlass末尾(位於PermGen內)移動到了java.lang.Class物件的末尾(位於普通Java heap內)。
“常量池”如果說的是SymbolTable / StringTable,這倆table自身原本就一直在native memory裡,是它們所引用的東西在哪裡更有意思。上面說了,7是把SymbolTable引用的Symbol移動到了native memory,而StringTable引用的java.lang.String例項則從PermGen移動到了普通Java heap。
傳送門:
jdk8之後永久代去哪了?

因和這篇文章說的相同(R說的SymbolTable 即為符號引用,StringTable即字串常量),故認為此理解正確,即 java7中,儲存在永久代的部分資料就已經轉移到Java Heap或者Native memory。但永久代仍存在於JDK 1.7中,並沒有完全移除。譬如符號引用(Symbols)轉移到了native memory;字串常量池(interned strings)轉移到了Java heap;類的靜態變數(class statics)轉移到了Java heap。

為什麼移除永久代?
1、字串存在永久代中,容易出現效能問題和記憶體溢位。
2、永久代大小不容易確定,PermSize指定太小容易造成永久代OOM
3、永久代會為 GC 帶來不必要的複雜度,並且回收效率偏低。
4、Oracle 可能會將HotSpot 與 JRockit 合二為一。

在JDK1.7中, 已經把原本放在永久代的字串常量池移出, 放在堆中. 為什麼這樣做呢?
因為使用永久代來實現方法區不是個好主意, 很容易遇到記憶體溢位的問題. 我們通常使用PermSize和MaxPermSize設定永久代的大小, 這個大小就決定了永久代的上限, 但是我們不是總是知道應該設定為多大的, 如果使用預設值容易遇到OOM錯誤。

類的元資料, 字串池, 類的靜態變數將會從永久代移除, 放入Java heap或者native memory。其中建議JVM的實現中將類的元資料放入 native memory, 將字串池和類的靜態變數放入java堆中. 這樣可以載入多少類的元資料就不在由MaxPermSize控制, 而由系統的實際可用空間來控制.

為什麼這麼做呢? 減少OOM只是表因, 更深層的原因還是要合併HotSpot和JRockit的程式碼, JRockit從來沒有一個叫永久代的東西, 但是執行良好, 也不需要開發運維人員設定這麼一個永久代的大小。當然不用擔心執行效能問題了, 在覆蓋到的測試中, 程式啟動和執行速度降低不超過1%, 但是這一點效能損失換來了更大的安全保障。

OpenJDK文件:

Success Metrics
Class metadata, interned Strings and class static variables will be moved from the permanent generation to either the Java heap or native memory.
(類元資料、內部字串和類靜態變數將從永久生成轉移到Java堆或本機記憶體。)
The code for the permanent generation in the Hotspot JVM will be removed.
(JVM中永久代的程式碼將被刪除。)
Application startup and footprint will not regress more than 1% as measured by a yet-to-be-chosen set of benchmarks.

Description
Move part of the contents of the permanent generation in Hotspot to the Java heap and the remainder to native memory.
(將Hotspot中永久代的部分內容移到Java堆中,其餘部分移到本地記憶體中。)
Hotspot’s representation of Java classes (referred to here as class meta-data) is currently stored in a portion of the Java heap referred to as the permanent generation. In addition, interned Strings and class static variables are stored in the permanent generation. The permanent generation is managed by Hotspot and must have enough room for all the class meta-data, interned Strings and class statics used by the Java application. Class metadata and statics are allocated in the permanent generation when a class is loaded and are garbage collected from the permanent generation when the class is unloaded. Interned Strings are also garbage collected when the permanent generation is GC’ed.

The proposed implementation will allocate class meta-data in native memory and move interned Strings and class statics to the Java heap. Hotspot will explicitly allocate and free the native memory for the class meta-data. Allocation of new class meta-data would be limited by the amount of available native memory rather than fixed by the value of -XX:MaxPermSize, whether the default or specified on the command line.
(這個被提議的實現將在本地記憶體中分配類元資料,並將內部的字串和類靜態轉移到Java堆中。Hotspot會顯式的為類元資料分配和釋放本地記憶體。新類元資料的分配將受可用本地記憶體的數量限制,而不是由-XX:MaxPermSize的值來固定,無論在命令列上是預設還是指定。)

Allocation of native memory for class meta-data will be done in blocks of a size large enough to fit multiple pieces of class meta-data. Each block will be associated with a class loader and all class meta-data loaded by that class loader will be allocated by Hotspot from the block for that class loader. Additional blocks will be allocated for a class loader as needed. The block sizes will vary depending on the behavior of the application. The sizes will be chosen so as to limit internal and external fragmentation. Freeing the space for the class meta-data would be done when the class loader dies by freeing all the blocks associated with the class loader. Class meta-data will not be moved during the life of the class.

參考:
Java8記憶體模型—永久代(PermGen)和元空間(Metaspace)
JAVA 方法區是在堆裡面嗎
Java8移除永久代
jdk8之後永久代去哪了?
Java8記憶體模型
OpenJDK文件