深入理解JAVA虛擬機《一》

Javan內存區域與內存溢出異常

一、運行時數據區域

1.程序計數器(線程私有)

  • 字節碼解釋器工作時就是通過改變這個計數器的值來選擇下一條需要執行的字節碼指令。
  • 線程執行main方法:計數器記錄正在執行的虛擬機字節碼指令地址。
  • Native方法:計數器值爲空。

2.Java虛擬機棧(線程私有)

  • 描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法出口等。

3.本地方法棧(線程私有)

  • 與虛擬機棧所發揮的作用是非常相似的,他們之間的區別的不過是本地方法棧則爲虛擬機使用到的Native方法服務,虛擬機棧爲虛擬機執行的Java方法(字節碼)服務。

4.Java堆(線程共享)

  • 此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都要在這分配內存管理。
  • Java堆是垃圾收集器管理的主要區域,也稱爲GC堆。
  • 從內存回收的角度來看,由於現在收集器基本都採用分代收集算法。
  • Java堆中細分:新生代和老生代,再細緻一點:Eden空間,From Survivor空間,To Survivor空間等。

5.方法區(線程共享)

  • 用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

6 運行時常量池

  • 運行時常量池是方法區的一部分。
  • 運行時常量池相對Class文件常量池的另外一個重要特徵是具備動態性。Java語言並不要求常量一定只有編譯器才能產生,也就是並非預置入Class文件中常量池的內容才能進入方法區運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發人員利用得最多得比較多的便是String類的intern()方法。

7.直接內存

  • 直接內存不屬於虛擬機運行時數據區的一部分,也不是虛擬機規範中定義的內存區域,但是會導致OOM異常出現。
  • 在JDK 1.4中新加入了NIO(New
    Input/Output)類,引入了一種基於通道與緩衝區的I/O方式,可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆中的DirectByteBuffer對象作爲這塊內存的引用進行操作。提高了性能,避免在Java堆和Native堆中來回賦值數據。
  • 本機直接內存的分配不受到Java堆大小的限制,但是會受到本機總內存大小以及處理器尋址空間的限制。

Java虛擬機運行時數據區

二、虛擬機對象

  1. 對象的創建

    虛擬機在遇到一個new指令時,首先將去檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,那必須先執行相應的類加載過程。

  2. 對象的內存佈局

    在虛擬機中,對象在內存中存儲的佈局可以分爲3塊區域:對象頭、實例數據和對齊填充。

    虛擬機的對象頭包括兩部分信息,第一部分用於存儲對象自身的運行時數據,如哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等。對象的另外一部分是類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。

三、OOM異常

  • Java堆溢出

    Java堆用於存儲對象實例,只要不斷地創建對象,並且保證GC Root到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那麼在對象數量到達最大堆的容量限制後就會產生內存溢出異常。

  • 虛擬機棧和本地方法棧溢出

    關於虛擬機棧和本地方法棧,在Java虛擬機規範中描述了這兩種異常:

    • 如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出Stack OverflowError異常。
    • 如果虛擬機在擴展棧時無法申請到足夠的內存空間,則拋出OutOfMemoryError異常。

四、方法區和運行時常量池溢出

String.intern()是一個Native方法,它的作用是:如果字符串常量池中已經包含一個等於此String對象的字符串,則返回代表池中這個字符串的String對象;否則,將此String對象包含的字符串添加到常量池中,並且返回此String對象的引用。

public static void main(String[] args) {
	// TODO Auto-generated method stub

	//	首次出現,記錄在常量池中,intern()返回了它的引用和StringBuilder創建的字符串案例是同一個。
	String str1 = new StringBuilder("計算機").append("軟件").toString();
	System.out.println(str1.intern() == str1);	//	true
	
	//	字符串常量池中已經有 java 的引用了,不符合首次出現原則。
	String str2 = new StringBuilder("ja").append("va").toString();
	System.out.println(str2.intern() == str2);	//	false
	
}

未完善,待更新…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章