天天看點

java8 jvm odf_jdk8中的jvm(轉載)

一:JVM中記憶體

JVM中記憶體通常劃分為兩個部分,分别為堆記憶體與棧記憶體,棧記憶體主要用運作線程方法

存放本地暫時變量與線程中方法運作時候須要的引用對象位址。

JVM全部的對象資訊都

存放在堆記憶體中。相比棧記憶體,堆記憶體能夠所大的多,是以JVM一直通過對堆記憶體劃分

不同的功能區塊實作對堆記憶體中對象管理。

堆記憶體不夠最常見的錯誤就是OOM(OutOfMemoryError)

棧記憶體溢出最常見的錯誤就是StackOverflowError。程式有遞歸調用時候最easy發生

二:堆記憶體劃分

在JDK7以及其前期的JDK版本号中。堆記憶體通常被分為三塊區域Nursery記憶體(young

generation)、長時記憶體(old generation)、永久記憶體(Permanent Generation for

VM Matedata),顯示範樣例如以下圖:

java8 jvm odf_jdk8中的jvm(轉載)

當中最上一層是Nursery記憶體,一個對象被建立以後首先被放到Nursery中的Eden内

存中,假設存活期超兩個Survivor之後就會被轉移到長時記憶體中(Old Generation)中

永久記憶體中存放着對象的方法、變量等中繼資料資訊。通過假設永久記憶體不夠。我們

就會得到例如以下錯誤:

java.lang.OutOfMemoryError: PermGen

而在JDK8中情況發生了明顯的變化,就是普通情況下你都不會得到這個錯誤,原因

在于JDK8中把存放中繼資料中的永久記憶體從堆記憶體中移到了本地記憶體(native memory)

中,JDK8中JVM堆記憶體結構就變成了例如以下:

java8 jvm odf_jdk8中的jvm(轉載)

這樣永久記憶體就不再占用堆記憶體。它能夠通過自己主動增長來避免JDK7以及前期版本号中

常見的永久記憶體錯誤(java.lang.OutOfMemoryError: PermGen),或許這個就是你的

JDK更新到JDK8的理由之中的一個吧。

當然JDK8也提供了一個新的設定Matespace記憶體

大小的參數。通過這個參數能夠設定Matespace記憶體大小,這樣我們能夠依據自己

項目的實際情況,避免過度浪費本地記憶體,達到有效利用。

-XX:MaxMetaspaceSize=128m 設定最大的元記憶體空間128兆

注意:假設不設定JVM将會依據一定的政策自己主動添加本地元記憶體空間。

假設你設定的元記憶體空間過小,你的應用程式可能得到下面錯誤:

java.lang.OutOfMemoryError: Metadata space

java1.8之前記憶體區域分為方法區、堆記憶體、虛拟機棧、本地方法棧、程式計數器。 下圖所示:

java8 jvm odf_jdk8中的jvm(轉載)

方法區(Method Area)與Java堆一樣,是各個線程共享的記憶體區域,它用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料。雖然Java虛拟機規範把方法區描述為堆的一個邏輯部分,但是它卻有一個别名叫做Non-Heap(非堆),目的應該是與Java堆區分開來。很多人都更願意把方法區稱為“永久代”(Permanent Generation)。從jdk1.7已經開始準備“去永久代”的規劃,jdk1.7的HotSpot中,已經把原本放在方法區中的靜态變量、字元串常量池等移到堆記憶體中。

在jdk1.8中,永久代已經不存在,存儲的類資訊、編譯後的代碼資料等已經移動到了元空間(MetaSpace)中,元空間并沒有處于堆記憶體上,而是直接占用的本地記憶體(NativeMemory)。

元空間的本質和永久代類似,都是對JVM規範中方法區的實作。不過元空間與永久代之間最大的差別在于:元空間并不在虛拟機中,而是使用本地記憶體。是以,預設情況下,元空間的大小僅受本地記憶體限制,但可以通過以下參數來指定元空間的大小:

-XX:MetaspaceSize,初始空間大小,達到該值就會觸發垃圾收集進行類型解除安裝,同時GC會對該值進行調整:如果釋放了大量的空間,就适當降低該值;如果釋放了很少的空間,那麼在不超過MaxMetaspaceSize時,适當提高該值。

-XX:MaxMetaspaceSize,最大空間,預設是沒有限制的。

除了上面兩個指定大小的選項以外,還有兩個與 GC 相關的屬性:

-XX:MinMetaspaceFreeRatio,在GC之後,最小的Metaspace剩餘空間容量的百分比,減少為配置設定空間所導緻的垃圾收集

-XX:MaxMetaspaceFreeRatio,在GC之後,最大的Metaspace剩餘空間容量的百分比,減少為釋放空間所導緻的垃圾收集

注意:如果不設定JVM将會根據一定的政策自動增加本地元記憶體空間。

如果你設定的元記憶體空間過小,你的應用程式可能得到以下錯誤:

java.lang.OutOfMemoryError: Metadata space

在Java7之前,HotSpot虛拟機中将GC分代收集擴充到了方法區,使用永久代來實作了方法區。這個區域的記憶體回收目标主要是針對常量池的回收和對類型的解除安裝。而在Java8中,已經徹底沒有了永久代,将方法區直接放在一個與堆不相連的本地記憶體區域,這個區域被叫做元空間。

常量池裡存儲着字面量和符号引用。

符号引用包括:1.類的全限定名,2.字段名和屬性,3.方法名和屬性。

字元串池裡的内容是在類加載完成,經過驗證,準備階段之後在堆中生成字元串對象執行個體,然後将該字元串對象執行個體的引用值存到string pool中(記住:string pool中存的是引用值而不是具體的執行個體對象,具體的執行個體對象是在堆中開辟的一塊空間存放的。)。 在HotSpot VM裡實作的string pool功能的是一個StringTable類,它是一個哈希表,裡面存的是駐留字元串(也就是我們常說的用雙引号括起來的)的引用(而不是駐留字元串執行個體本身),也就是說在堆中的某些字元串執行個體被這個StringTable引用之後就等同被賦予了”駐留字元串”的身份。這個StringTable在每個HotSpot VM的執行個體隻有一份,被所有的類共享。

1.字元串池常量池在每個VM中隻有一份,存放的是字元串常量的引用值,存放在堆中.

2.class常量池是在編譯的時候每個class都有的,在編譯階段,存放的是常量的符号引用。

3.運作時常量池是在類加載完成之後,将每個class常量池中的符号引用值轉存到運作時常量池中,也就是說,每個class都有一個運作時常量池,類在解析之後,将符号引用替換成直接引用,與全局常量池中的引用值保持一緻。