基礎(chǔ)知識(shí)
JVM只不過是運(yùn)行在你系統(tǒng)上的另一個(gè)進(jìn)程而已尉间,這一切的魔法始于一個(gè)java命令。正如任何一個(gè)操作系統(tǒng)進(jìn)程那樣缺虐,JVM也需要內(nèi)存來完成它的運(yùn)行時(shí)操 作割坠。記住——JVM本身是硬件的一層軟件抽象,在這之上才能夠運(yùn)行Java程序陨簇,也才有了我們所吹噓的平臺(tái)獨(dú)立性以及WORA(一次編寫吐绵,處處運(yùn)行)。
快速過一遍JVM的內(nèi)存結(jié)構(gòu)
正如虛擬機(jī)規(guī)范所說的那樣河绽,JVM中的內(nèi)存分為5個(gè)虛擬的區(qū)域己单。
- 堆
- 方法區(qū)(非堆)
- JVM棧
- 本地棧
- PC寄存器
堆
你的Java程序中所分配的每一個(gè)對(duì)象都需要存儲(chǔ)在內(nèi)存里。堆是這些實(shí)例化的對(duì)象所存儲(chǔ)的地方耙饰。是的——都怪new操作符纹笼,是它把你的Java堆都占滿了的!
它由所有線程共享
當(dāng)堆耗盡的時(shí)候苟跪,JVM會(huì)拋出java.lang.OutOfMemoryError 異常
堆的大小可以通過JVM選項(xiàng)-Xms和-Xmx來進(jìn)行調(diào)整
堆被分為:
Eden區(qū) —— 新對(duì)象或者生命周期很短的對(duì)象會(huì)存儲(chǔ)在這個(gè)區(qū)域中廷痘,這個(gè)區(qū)的大小可以通過-XX:NewSize和-XX:MaxNewSize參數(shù)來調(diào)整蔓涧。新生代GC(垃圾回收器)會(huì)清理這一區(qū)域。
Survivor區(qū) —— 那些歷經(jīng)了Eden區(qū)的垃圾回收仍能存活下來的依舊存在引用的對(duì)象會(huì)待在這個(gè)區(qū)域笋额。這個(gè)區(qū)的大小可以由JVM參數(shù)-XX:SurvivorRatio來進(jìn)行調(diào)節(jié)元暴。
老年代 —— 那些在歷經(jīng)了Eden區(qū)和Survivor區(qū)的多次GC后仍然存活下來的對(duì)象(當(dāng)然了,是拜那些揮之不去的引用所賜)會(huì)存儲(chǔ)在這個(gè)區(qū)里鳞陨。這個(gè)區(qū)會(huì)由一個(gè)特殊的垃圾回收器來負(fù)責(zé)昨寞。年老代中的對(duì)象的回收是由老年代的GC(major GC)來進(jìn)行的。
方法區(qū)
也被稱為非堆區(qū)域(在HotSpot JVM的實(shí)現(xiàn)當(dāng)中)
它被分為兩個(gè)主要的子區(qū)域
持久代 —— 這個(gè)區(qū)域會(huì)存儲(chǔ)包括類定義厦滤,結(jié)構(gòu)援岩,字段,方法(數(shù)據(jù)及代碼)以及常量在內(nèi)的類相關(guān)數(shù)據(jù)掏导。它可以通過-XX:PermSize及 -XX:MaxPermSize來進(jìn)行調(diào)節(jié)享怀。如果它的空間用完了,會(huì)導(dǎo)致java.lang.OutOfMemoryError: PermGen space的異常趟咆。
代碼緩存——這個(gè)緩存區(qū)域是用來存儲(chǔ)編譯后的代碼添瓷。編譯后的代碼就是本地代碼(硬件相關(guān)的),它是由JIT(Just In Time)編譯器生成的值纱,這個(gè)編譯器是Oracle HotSpot JVM所特有的鳞贷。
JVM棧
- 和Java類中的方法密切相關(guān)
- 它會(huì)存儲(chǔ)局部變量以及方法調(diào)用的中間結(jié)果及返回值
- Java中的每個(gè)線程都有自己專屬的棧,這個(gè)棧是別的線程無法訪問的虐唠。
- 可以通過JVM選項(xiàng)-Xss來進(jìn)行調(diào)整
本地棧
- 用于本地方法(非Java代碼)
- 按線程分配
PC寄存器
- 特定線程的程序計(jì)數(shù)器
- 包含JVM正在執(zhí)行的指令的地址(如果是本地方法的話它的值則未定義)
- 好吧搀愧,這就是JVM內(nèi)存分區(qū)的基礎(chǔ)知識(shí)了。現(xiàn)在再說說持久代這個(gè)話題吧疆偿。
那么持久代上哪去了咱筛?
事實(shí)上,持久代已經(jīng)被徹底刪除了杆故,取代它的是另一個(gè)內(nèi)存區(qū)域也被稱為元空間迅箩。
為什么移除持久代
- 它的大小是在啟動(dòng)時(shí)固定好的——很難進(jìn)行調(diào)優(yōu)。-XX:MaxPermSize处铛,設(shè)置成多少好呢饲趋?
- HotSpot的內(nèi)部類型也是Java對(duì)象:它可能會(huì)在Full GC中被移動(dòng),同時(shí)它對(duì)應(yīng)用不透明撤蟆,且是非強(qiáng)類型的奕塑,難以跟蹤調(diào)試,還需要存儲(chǔ)元數(shù)據(jù)的元數(shù)據(jù)信息(meta-metadata)枫疆。
- 簡化Full GC:每一個(gè)回收器有專門的元數(shù)據(jù)迭代器爵川。
- 可以在GC不進(jìn)行暫停的情況下并發(fā)地釋放類數(shù)據(jù)。
- 使得原來受限于持久代的一些改進(jìn)未來有可能實(shí)現(xiàn)
元空間 —— 快速入門
持久代的空間被徹底地刪除了息楔,它被一個(gè)叫元空間的區(qū)域所替代了寝贡。持久代刪除了之后扒披,很明顯,JVM會(huì)忽略PermSize和MaxPermSize這兩個(gè)參數(shù)圃泡,還有就是你再也看不到j(luò)ava.lang.OutOfMemoryError: PermGen error的異常了碟案。
JDK 8的HotSpot JVM現(xiàn)在使用的是本地內(nèi)存來表示類的元數(shù)據(jù),這個(gè)區(qū)域就叫做元空間颇蜡。
元空間的特點(diǎn):
- 充分利用了Java語言規(guī)范中的好處:類及相關(guān)的元數(shù)據(jù)的生命周期與類加載器的一致价说。
- 每個(gè)加載器有專門的存儲(chǔ)空間
- 只進(jìn)行線性分配
- 不會(huì)單獨(dú)回收某個(gè)類
- 省掉了GC掃描及壓縮的時(shí)間
- 元空間里的對(duì)象的位置是固定的
- 如果GC發(fā)現(xiàn)某個(gè)類加載器不再存活了,會(huì)把相關(guān)的空間整個(gè)回收掉
元空間的內(nèi)存分配模型
- 絕大多數(shù)的類元數(shù)據(jù)的空間都從本地內(nèi)存中分配
- 用來描述類元數(shù)據(jù)的類也被刪除了
- 分元數(shù)據(jù)分配了多個(gè)虛擬內(nèi)存空間
- 給每個(gè)類加載器分配一個(gè)內(nèi)存塊的列表风秤。塊的大小取決于類加載器的類型; sun/反射/代理對(duì)應(yīng)的類加載器的塊會(huì)小一些
- 歸還內(nèi)存塊鳖目,釋放內(nèi)存塊列表
- 一旦元空間的數(shù)據(jù)被清空了,虛擬內(nèi)存的空間會(huì)被回收掉
- 減少碎片的策略
總結(jié)
- Hotspot中的元數(shù)據(jù)現(xiàn)在存儲(chǔ)到了元空間里缤弦。mmap中的內(nèi)存塊的生命周期與類加載器的一致领迈。
- 類指針壓縮空間(Compressed class pointer space)目前仍然是固定大小的,但它的空間較大
- 可以進(jìn)行參數(shù)的調(diào)優(yōu)碍沐,不過這不是必需的狸捅。
- 未來可能會(huì)增加其它的優(yōu)化及新特性。比如累提, 應(yīng)用程序類數(shù)據(jù)共享尘喝;新生代GC優(yōu)化,G1回收器進(jìn)行類的回收斋陪;減少元數(shù)據(jù)的大小朽褪,以及JVM內(nèi)部對(duì)象的內(nèi)存占用量。