JVM 內(nèi)存模型
根據(jù) JVM 規(guī)范,JVM 內(nèi)存共分為虛擬機棧、堆、方法區(qū)、程序計數(shù)器、本地方法棧五個部分。
線程私有:虛擬機棧,本地方法棧,程序計數(shù)器。
線程共享:堆,方法區(qū)。
JDK 1.7 中的JVM內(nèi)存結(jié)構(gòu)圖

堆和方法區(qū)連在了一起,但這并不能說堆和方法區(qū)是一起的,它們在邏輯上依舊是分開的。但在物理上來說,它們又是連續(xù)的一塊內(nèi)存。也就是說,方法區(qū)和Eden和老年代是連續(xù)的。
程序計數(shù)器(Program Counter Register)
概念:當前線程所執(zhí)行的字節(jié)碼的行號指示器。
功能:在虛擬機的概念模型里,字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等功能都需要依賴這個計數(shù)器來完成。
注:唯一一個在java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。
Java虛擬機棧
概念:虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀用于存儲局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口等信息。每一個方法從調(diào)用直至執(zhí)行完成的過程,就對應(yīng)著一個棧幀在虛擬機棧中入棧到出棧的過程。
局部變量表:存放了編譯期可知的基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等同于對象本身,可能是一個指向?qū)ο笃鋵嵉刂返囊弥羔?,也可能是指向一個代表對象的句柄或其他與此對象相關(guān)的位置)和一條指向了字節(jié)碼指令的地址(returnAddress類型)。
異常:
StackOverflowError:線程請求的棧的深度大于虛擬機所允許的深度。
OutOfMemoryError: 虛擬機棧動態(tài)擴展時,無法申請到足夠的內(nèi)存。
本地方法棧
與虛擬機棧的作用非常相似,主要與虛擬機用到的 Native 方法相關(guān)。
Java堆
是java虛擬機所管理內(nèi)存中最大的一塊,是線程共享的一塊內(nèi)存區(qū)域。幾乎所有的對象實例以及數(shù)組都要在堆上分配,但是隨著JIT編譯器(http://www.itdecent.cn/p/11b91da0b0a0)的發(fā)展與逃逸分析技術(shù)逐漸成熟,棧上分配、標量替換優(yōu)化技術(shù),導(dǎo)致所有的對象都分配在堆上也不是那么絕對了。
垃圾回收:java堆是垃圾收集器管理的主要區(qū)域,也稱作GC堆。
方法區(qū)
概念:方法區(qū)是線程共享的內(nèi)存區(qū)域,存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。在java虛擬機規(guī)范中,方法區(qū)描述為堆的一個邏輯部分,為了與java堆區(qū)分開來,方法區(qū)也稱作Non-Heap(非堆)。
對于習(xí)慣了在HotSpot虛擬機上開發(fā)、部署的程序員來說,很多都愿意將方法區(qū)稱作永久代(Permanet Generation,也稱PermGen)。
本質(zhì)上來講兩者并不等價,僅因為Hotspot將GC分代擴展至方法區(qū),或者說使用永久代來實現(xiàn)方法區(qū)。在其他虛擬機上是沒有永久代的概念的。也就是說方法區(qū)是規(guī)范,永久代是Hotspot針對該規(guī)范進行的實現(xiàn)。

永久代和堆是相互隔離的,但它們使用的物理內(nèi)存是連續(xù)的。
永久代的垃圾收集是和老年代捆綁在一起的,因此無論誰滿了,都會觸發(fā)永久代和老年代的垃圾收集。
但在Java7中永久代中存儲的部分數(shù)據(jù)已經(jīng)開始轉(zhuǎn)移到Java Heap或Native Memory中了。比如,符號引用(Symbols)轉(zhuǎn)移到了Native Memory;字符串常量池(interned strings)轉(zhuǎn)移到了Java Heap;類的靜態(tài)變量(class statics)轉(zhuǎn)移到了Java Heap。
在Java8中,Hotspot取消了永久代。永久代的參數(shù)-XX:PermSize和-XX:MaxPermSize也隨之失效。
元空間(Metaspace)
對于Java8,HotSpots取消了永久代,那么是不是就沒有方法區(qū)了呢?當然不是,方法區(qū)只是一個規(guī)范,只不過它的實現(xiàn)變了。
在Java8中,元空間(Metaspace)登上舞臺,方法區(qū)存在于元空間(Metaspace)。同時,元空間不再與堆連續(xù),而且是存在于本地內(nèi)存(Native memory)。

本地內(nèi)存(Native memory),也稱為C-Heap,是供JVM自身進程使用的。當Java Heap空間不足時會觸發(fā)GC,但Native memory空間不夠卻不會觸發(fā)GC。
針對Java8的調(diào)整,我們再次對內(nèi)存結(jié)構(gòu)圖進行調(diào)整。

元空間的本質(zhì)和永久代類似,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)。不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機中,而是使用本地內(nèi)存。因此,默認情況下,元空間的大小僅受本地內(nèi)存限制,但可以通過以下參數(shù)來指定元空間的大?。?/p>
元空間存在于本地內(nèi)存,意味著只要本地內(nèi)存足夠,它不會出現(xiàn)像永久代中“java.lang.OutOfMemoryError: PermGen space”這種錯誤。看上圖中的方法區(qū),是不是“膨脹”了。
默認情況下元空間是可以無限使用本地內(nèi)存的,但為了不讓它如此膨脹,JVM同樣提供了參數(shù)來限制它使用的使用。
-XX:MetaspaceSize,初始空間大小,達到該值就會觸發(fā)垃圾收集進行類型卸載,同時GC會對該值進行調(diào)整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那么在不超過MaxMetaspaceSize時,適當提高該值。
-XX:MaxMetaspaceSize,最大空間,默認是沒有限制的。
除了上面兩個指定大小的選項以外,還有兩個與 GC 相關(guān)的屬性:
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空間容量的百分比,減少為分配空間所導(dǎo)致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空間容量的百分比,減少為釋放空間所導(dǎo)致的垃圾收集
總結(jié)
為什么要做這個轉(zhuǎn)換?
1、字符串存在永久代中,容易出現(xiàn)性能問題和內(nèi)存溢出。
2、類及方法的信息等比較難確定其大小,因此對于永久代的大小指定比較困難,太小容易出現(xiàn)永久代溢出,太大則容易導(dǎo)致老年代溢出。
3、永久代會為 GC 帶來不必要的復(fù)雜度,并且回收效率偏低。
4、Oracle 可能會將HotSpot 與 JRockit 合二為一。
轉(zhuǎn)自:
1.https://www.cnblogs.com/secbro/p/11718987.html
2.https://www.cnblogs.com/paddix/p/5309550.html
參考:《深入理解java虛擬機》