Java虛擬機器記憶體地區詳解,java虛擬機器詳解
眾所周知,Java程式運行於Java虛擬機器(JVM)上,那麼,JVM啟動並執行時候記憶體是如何分配的呢?程式中各部分變數都儲存在記憶體的哪個部分,又如何訪問,下面,就讓我來給大家講解Java虛擬機器記憶體地區。
為什麼需要瞭解Java虛擬機器記憶體地區
相對於C++程式員,因為虛擬機器的自動記憶體管理機制的存在,Java程式員很多時候並不需要去擔心記憶體的泄露和記憶體溢出的問題。但是正是因為把記憶體的控制權交給了JVM,一旦出現記憶體泄露和溢出的問題,如果不瞭解虛擬機器怎麼使用記憶體,就很難排查錯誤。
記憶體泄露:一些對象在使用完畢之後,系統沒有將對象佔據的記憶體回收或者沒有及時回收,造成浪費。
記憶體溢出:就是希望給某對象或變數分配記憶體的時候,記憶體不夠
Java虛擬機器記憶體地區解析
如所示,JVM所管理的記憶體包括以下幾個運行時資料區
程式計數器:它是線程私人的記憶體。它的記憶體空間較小,作用可以看做是當前線程所執行的位元組碼的行號指標。位元組碼解析器工作時通過改變這個程式計數器的值來選取下一條需要執行的位元組碼指令。(也就是保證線程能夠按照程式員希望的順序執行)
如果線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;如果正在執行的是一個Native方法,這個計數器為空白。本記憶體地區是唯一一個在Java虛擬機器規範中沒有規定任何OutOfMemoryError異常情況的記憶體地區。
這部分記憶體為什麼是私人的呢?因為在任意某一時刻,一個處理器只會執行一個線程中的指令。因此,為了線程切換後能夠恢複到正確的執行位置,每條線程都需要有一個獨立的程式計數器,各線程只見的計數器互不影響,隔離儲存區 (Isolated Storage)。
虛擬機器棧:它也是線程私人的記憶體,它也是我們平時笼統說法中的“棧記憶體”。虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法被執行的時候都會同時建立一個棧幀用於儲存局部記憶體表、操作棧、動態連結、方法出口等資訊。每一個方法被調用直至執行完成的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。但一個方法執行完後,這個棧幀就會被記憶體回收,這時,這個方法裡面所定義的對象以及一些final變數等就變成了沒有用處了,所以他們所佔據的記憶體空間必須回收,否則就是記憶體泄露。
局部變數表存放了編譯期可知的各種基礎資料型別 (Elementary Data Type)、對象引用(reference類型,它不等同於對象本身,根據不同的虛擬機器實現,它可能是一個指向對象起始地址的引用指標,也可能是指向一個代表對象的控制代碼或其他與此對象相關的位置)和retrunAddress類型(指向一條位元組碼指令的地址)
虛擬機器棧記憶體地區存在兩種異常狀況:
1、如果線程請求的棧深度(可以理解為棧的長度)大於虛擬機器所允許的深度,將會拋出StackOverflowError異常
2、如果虛擬機器棧可以動態擴充,但當擴充時無法申請到足夠的記憶體時將會拋出OutOfMemoryError異常
本地方法棧:它也是線程私人的記憶體。它與虛擬機器棧所發揮的功能和作用相似,只是虛擬機器棧為虛擬機器所執行的方法服務,而本地方法棧則為虛擬機器使用到的Native方法服務。
Java堆:它是線程共用的,它也是我們笼統說法中的“堆記憶體”。它是虛擬機器所管理的記憶體中最大的一塊,在虛擬機器啟動的時候建立。此記憶體地區存在的唯一目的就是存放對象執行個體,幾乎所有的對象執行個體以及數組都在這裡分配記憶體
Java堆是垃圾收集器管理的主要區域,因為幾乎所有的對象執行個體都在這裡分配記憶體,當對象使用完畢,垃圾收集器管理器應該將這部分記憶體回收
Java堆可以處於物理上不連續的記憶體空間中,只是邏輯上連續即可。如果在堆中沒有記憶體完成對象執行個體的分配,並且堆也無法在擴充時,將會拋出OutOfMemoryError異常
方法區:它是線程共用的 。它用於儲存已經被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的代碼等資料。
相對於Java堆是垃圾收集器管理的主要區域,Java虛擬機器對方法區的限制非常寬鬆,它還可以選擇不實現記憶體回收。這個地區的記憶體回收目標主要是針對常量池的回收和對類型的卸載。
常量池是方法區的一部分,用於存放編譯期產生的各種字面量和符號引用,這部分內容將在類載入後存放在方法區運行時常量池中。
當方法區無法滿足記憶體配置需求時,會拋出OutOfMemoryError異常
直接記憶體:直接記憶體並不是JVM管理的記憶體,可以這樣理解,直接記憶體,就是JVM以外的機器記憶體,比如,你有4G的記憶體,JVM佔用了1G,則其餘的3G就是直接記憶體,JDK中有一種基於通道(Channel)和緩衝區(Buffer)的記憶體配置方式,將由C語言實現的native函數庫分配在直接記憶體中,用儲存在JVM堆中的DirectByteBuffer來引用。由於直接記憶體收到本機器記憶體的限制,所以也可能出現OutOfMemoryError的異常。
至此,我們已經知道Java程式中各部分變數都儲存在JVM記憶體的哪個部分,以及各記憶體地區的作用,是不是有一種豁然開朗的感覺
我們將在下一篇博文中介紹,JVM如何?對象的訪問