JVM學習筆記-Java虛擬機的運行時數據區域

1、運行時數據區域

  Java虛擬機在執行Java程序的過程中會把他所管理的內存劃分爲若干個不同的數據區域。這些區域都有各自的用途,以及創建和銷燬的時間,有的區域隨着虛擬機進程的啓動而啓動,有些區域則依賴用戶線程的啓動和結束而建立和銷燬。根據《Java虛擬機規範(JavaSE 7版)》的規定,Java虛擬機所管理的內存將會包括以下幾個運行時數據區域,如下圖所示:
在這裏插入圖片描述

2、程序計數器

  程序計數器是一塊較小的內存空間,可以看作是當前線程所執行的字節碼的行號指示器。
  每個線程都有一個獨立的程序計數器,各條線程之間的計數器也互不影響,獨立存儲,所以程序計數器是“線程私有”的。
  當執行Java方法時,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址,如果執行的是Native方法,這個計數器則爲空(undefined)。
  此內存區域是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。

3、Java虛擬機棧

  Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的。它的生命週期與線程相同。Java虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的時候都會創建一個棧幀(Stack Frame)用來存儲局部變量表、操作數棧、動態鏈接、方法出口燈信息。每個方法從調用到執行完成的過程,其實就對應着一個棧幀在虛擬機中入棧到出棧的過程。
  局部變量表存放了編譯期可知的各種基本數據類型、對象引用和returnAddress類型。其中,64位長度的long和double類型的數據會佔用2個局部變量空間(Slot),其餘的數據類型只佔用1個。局部變量表所需要的內存空間在編譯期完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。
  異常情況:如果線程請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError異常;如果虛擬機棧可以動態擴展,如果擴展時無法申請到足夠內存,就會拋出OutOfMemoryError異常。

4、本地方法棧

  本地方法棧和Java虛擬機棧類似,只是服務的對象不一樣而已。前者爲虛擬機使用到Native方法服務,而後者則是爲虛擬機執行Java方法服務。

5、Java堆

  Java堆(Java Heap)是Java虛擬機所管理的內存中最大的一塊。是被所有線程共享的一塊內存區域,在虛擬機啓動時被創建。Java堆的唯一目的就是存放對象實例。
  Java堆是垃圾收集器管理的主要區域,因此也被成爲“GC堆”(Garbage Collected Heap)。根據垃圾收集器採用的收集算法,又可以細分成新生代和老生代
  根據Java虛擬機的規定,Java堆可以處於物理上不連續的內存空間中,只要邏輯上是連續的即可。當在堆中沒有內存完成實例分配,並且堆也無法再擴展時,就會拋出OutOfMemoryError異常。

6、方法區

  方法區也是線程共享的內存區域,它用來存儲已被虛擬機加載的類型信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
  根據Java虛擬機規範的規定,當方法區無法滿足內存分配需求時,將拋出OutOfMemoryError異常。

  運行時常量池是方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯期生成的各類字面量和符號引用,這部分內容將在了加載後進入方法區的運行時常量池中存放。當常量池無法再申請到內存時,也會拋出OutOfMemoryError異常。

7、直接內存

  直接內存並不是虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域。
  在Jdk 1.4中新加入了NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,它可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆中的DirctByteBuffer對象作爲這塊內存的引用進行操作。這樣能夠在一些場景中顯著提高性能,因此避免了在Java堆和Native堆中來回複製數據。
  本機直接內存分配不受Java堆大小的限制,但是還是會收本機總內存的大小以及處理器尋址空間的限制。因此,當各個內存區域綜合大於物理內存限制,從而導致動態擴展時,出現OutOfMemoryError異常。

注:以上知識點,基本上都是來至於《深入理解JVM虛擬機-JVM高級特性與最佳實踐》一書。

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