標籤:9.png ons 通道 規範 錯誤 深度 溢出 檔案 堆記憶體
Java虛擬機器在執行java程式的過程中會把他管理的記憶體劃分為若干個不同的資料區域各自用途、建立以及銷毀時間各不相同。有的隨著虛擬機器進行的啟動而存在,有的地區依賴於線程的啟動和結束而建立以及銷毀。
1.程式計數器
Jvm將這個計數看作當前線程執行某條位元組碼的行號指標,會根據計數器的值來選取下一條需要執行的位元組碼指令。這個屬於線程私人,不可共用,如果共用會導致計數混亂,無法準確的執行當前線程需要執行的語句。
如果一個線程正在執行java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;如果執行的是Native方法,這個計數器的值為空白(Undefined)。
該地區是唯一一個在java虛擬機器規範中沒有規定任何OutOfMemoryError的情況。
註:一個Native Method就是一個Java調用非Java代碼的介面。一個Native Method是這樣一個Java的方法:該方法的實現由非Java語言實現,比如C或C++。
2.Java虛擬機器棧
棧記憶體就是指虛擬機器棧。Java中每一個方法從調用直至執行完成的過程,就對應著一個棧幀在虛擬機器棧中入棧到出棧的過程。
虛擬機器棧表述的是Java方法執行的記憶體模型:每個方法在執行的同時,都會建立一個棧幀(Stack Frame),用於存放局部變數表、運算元棧、動態連結、方法出口等。口頭所說的“棧記憶體”就是虛擬機器棧中局部變數表部分,局部變數表存放了編譯器可知的各種基礎資料型別 (Elementary Data Type)(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,不同於對象本身,可能指向一個對象起始地址的引用指標,也可能指向一個代表對象的控制代碼或其他與此對象相關的位置)和returnAddress類型(指向了一條位元組碼指令的地址)。
其中64位長度的long和double類型的資料會佔用兩個局部變數空間,其餘資料類型只有一個。局部變數表所需的記憶體空間在編譯期間完成分配。
如果線程請求的棧深度大於虛擬機器所允許的深度,將拋出StackOverflowError異常;如果虛擬機器棧可以動態擴充(當前大部分的Java虛擬機器都可動態擴充,只不過Java虛擬機器規範中也允許固定長度的虛擬機器棧),如果擴充時無法申請到足夠的記憶體,就會拋出OutOfMemoryError異常。
3.本地方法棧
虛擬機器棧用來執行java方法,而本地方法棧用來執行本地方法。
拋出異常的情況和虛擬機器棧一樣會拋出StackOverflowError異常和OutOfMemoryError異常。
4.Java堆
是jvm中記憶體最大、線程共用的一塊地區。所有線程共用的一塊記憶體地區,在虛擬機器啟動時建立。唯一的目的是儲存物件執行個體。這裡也是垃圾收集器主要收集的地區(GC堆:GarbageCollected Heap)。由於現代垃圾收集器都採用的是分代收集演算法,所以java堆也分為新生代和老年代。
線程共用的Java堆中可能劃分出多個線程私人的分配緩衝區(Thread Local Allocation Buffer,TLAB),與存放內容無關,都存的是對象執行個體。
Java堆可以處於物理上不連續的記憶體空間中,只要邏輯上連續即可。可以通過參數-Xmx(jvm最大可用記憶體)和-Xms(jvm初始記憶體)來調整堆記憶體,如果在堆中沒有記憶體完成執行個體分配,並且堆也擴大至無法繼續擴充時,會出現OutOfMemoryError的錯誤。
5.方法區("永久代")
Jvm中記憶體共用的一片地區,用來存儲類資訊、常量、靜態變數、class檔案。垃圾收集器也會對這部分地區進行回收,比如常量池的清理和類型的卸載,但是效果不理想。其 實現想像堆一樣-XX:MaxPermSize設定管理這部分記憶體,而不用專門為該區進行記憶體管理編寫代碼。但是容易造成記憶體溢出,有逐步改為Native Memory來實現方法區的規划了,JDK1.7中已經把原本放在永久代的字串常量池移出。
方法區記憶體不夠用的時候,也會拋出OutOfMemoryError錯誤。
6.運行時常量池
運行時常量池是方法區的一部分(Runtime Constant Pool)。Class檔案除了有類的版本、欄位、方法、介面等資訊,還有一項資訊是常量池(Constant Pool Table),用於存放編譯期產生的各種字面量和符號引用,這些內容在類載入後進入方法區的運行時常量池存放。
Java虛擬機器堆Class檔案有嚴格規定,但是對運行時常量池沒有,一般來說除了儲存Class檔案中描述的符號引用外,還會把翻譯出來的直接引用也儲存在運行時常量池中。運行期間也可將新的常量放入池中,如String類的inter()方法。
屬於方法區,自然也受方法區限制,當常量池無法再申請到記憶體時會拋出OutOfMemoryError錯誤。
7.直接記憶體
直接記憶體(Direct Memory)不是虛擬機器運行時資料區的一部分,也不是虛擬機器規範中定義的資料區域。
在JDK1.4中加入了NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,可以使用Native函數庫直接分配堆外記憶體,通過一個儲存在Java堆中的DirectByteBuffer對象作為這塊記憶體的引用進行操作。在一些情境中避免在Java堆和Native堆中來回複製資料。
本級直接記憶體不會受Java堆大小限制,受本機總記憶體大小以及處理器定址空間限制,動態擴充時超過實體記憶體限制會出現OutOfMemoryError異常
總體概括
java運行時資料區域