天天看點

JVM-運作時資料區域(2) JVM-運作時資料區域(2)(3)運作時常量池:直接記憶體

JVM-運作時資料區域(2)

線程共享:

(1)java堆:

java堆是java虛拟機所管理記憶體中最大的一塊,java堆是所有線程共享的一塊記憶體區域。

 java堆在虛拟機啟動的時候就建立了(java虛拟機桟的桟幀中的局部變量表在編譯時期就分好了記憶體)。java堆的是用來存放對象執行個體的。所有的對象執行個體和數組都在java堆中分布。

 java堆是垃圾收集器管理的主要區域。java堆可以細分為:老年代和新生代;再細緻一點的劃分可以是Eden空間,From Survivor空間,To Survivor空間。

從記憶體配置設定的角度來看,線程共享的java堆中可能劃分出多個線程私有的配置設定緩沖區(TLAB,該區域給每個對象 配置設定記憶體時使用,後面會有介紹)

java堆在實體上可以處于不連續的記憶體空間中,隻要邏輯上連續就可以,就像我們的磁盤空間,可以通過-Xmx和-Xms來控制 擴充java堆。

如果在堆中沒有記憶體完成執行個體配置設定,并且堆也無法擴充的時候,會抛出OutOfMemoryError異常。

(2)方法區:

方法區與java堆一樣,是一個線程共享的區域,它用于存儲已經被虛拟機加載的類,常量,靜态變量,即使編譯器編譯後的代碼等資料。

在虛拟機規範中,方法區是java堆的一個邏輯部分,但是實際上方法區并不是堆。

很多人将方法區稱為永久代,是因為在HotSpot虛拟機中将GC的分代收集器擴充至方法區,确切的說是用永久代來實作方法區。原則上怎麼實作方法區屬于虛拟機的細節,不受虛拟機控制,但是使用永久代實作方法區并不是一個好的主意,因為永久代更容易遇到記憶體溢出的問題,在HotSpot虛拟機的後續發展上,已經準備放棄永久代而改為Native Memory來實作方法區的規劃,jdk1.7将常量池移出方法區。

Java虛拟機規範對方法區的限制非常寬松,其和java堆一緻,同樣不需要連續的記憶體來存儲,并且可以選擇固定大小或者擴充記憶體,此外,還可以選擇不實作垃圾收集。

垃圾收集器在方法區中出現愛你的較少,但是并不代表存在方法區中的資料不會被回收,這個區域的記憶體回收目标主要針對的是常量池的回收和對類型的解除安裝,尤其是對類型解除安裝的回收是相當的苛刻,回收成績難以令人滿意,但是方法區的記憶體回收是必須的。

當方法區無法滿足記憶體的配置設定的需求的時候,有時候會抛出OutOfMemoryError異常。

(3)運作時常量池:

運作時常量池是方法區的一部分,是以也是線程共有

首先,先進行聲明運作時常量池和常量池并不是一回事,常量池在class檔案中存儲,在類被加載後,常量池會進入方法的運作時常量池。class檔案的常量池中主要存放各種字面量和符号引用。

字面量:就是常量,字元串等 符号引用:指的就是用一組符号來描述引用的目标(比如user類中引用Person類,則用person這個字元串來表示Person類所在的位址) 直接引用:直接指向目标的指針。

java虛拟機對class檔案的每一個部分的格式都有嚴格的規定,但對于運作時常量池,java虛拟機沒有任何規定細節。

運作時常量池相比較class檔案常量池的差別是,運作時常量池具備動态性,也就是說java語言并不是要求常量隻能在編譯時期産生,也就是說不隻是編譯後的class檔案的常量池可以進入運作時常量池,運作期間可以将新的常量放入運作時常量池,如String類的intern()方法。

運作時常量池作為方法區的一部分,當常量池無法得到足夠的記憶體時,報OutOfMemoryError。

直接記憶體

運作時記憶體不是虛拟機運作時資料的一部分,但是使用頻繁,且也會抛出OutOfMemoryError異常。

在jdk1.4之後新加入了一個NIO類,引入了一種基于通道和緩沖區IO方式,它可以使用Native函數庫直接配置設定堆外記憶體,并且在将指向該記憶體的引用存儲在java的堆中,這樣提高了性能,避免了在java堆和Native堆中來回複制資料。

本地記憶體雖然不會收到java堆大小的限制,但是會受到計算機的記憶體限制,是以在無法申請到足夠的直接記憶體空間時,會抛出OutOfMemoryError異常。