Java虛拟機在執行java程式的過程中會把它所管理的記憶體劃分為若幹個不同的資料區域,這些資料區域有各自的用途以及建立時間和銷毀時間,有的區域随着虛拟機程序的啟動而一直存在,有些區域則依賴使用者線程的啟動和結束而建立和銷毀,Java虛拟機所管理的記憶體包括以下幾個運作時資料區域,如圖所示。

1.程式計數器
程式計數器是一塊很小的記憶體空間,可以看做是目前線程所執行位元組碼的行号訓示器 ,在Java虛拟機概念模型中,位元組碼訓示器工作時就是通過改變這個計數器的值來選區下一條需要執行的位元組碼指定,它是程式控制流的訓示燈,分支、循環、跳轉、異常處理、線程恢複等基礎功能都需要依賴這個計數器完成。
Java虛拟機的多線程是通過線程輪流切換、配置設定處理器執行時間的方式來時間,在任一時刻,一個處理器隻能執行一條線程中的指令,是以,為了線程切換後能回複到正确的執行位置,每條線程都需要一個獨立的程式計數器,各條線程互不影響,獨立存儲,這類記憶體成為“線程私有”的記憶體。
如果線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛拟機位元組碼指令的位址,如果正在執行的是本地(native)方法,這個計數器值則應為(undefined),此記憶體區域是唯一一個在《Java虛拟機規範》中沒有規定任何OOM(OutOfMemoryError)異常的區域。
注:本地(native)方法,不在jvm範疇之内,沒法編譯它和給它偏移量,同時無法生成位元組碼,是以不存在指令位址,即JVM擷取不到native實作,隻能通過系統指令去調用native方法(調用時相當于重開一個線程),在這裡可以了解為C/C++暴露出來的接口。
2.Java虛拟機棧
同程式計數器一樣,Java虛拟機棧(Java Virtual Machine Stack)也是線程私有的,它的生命周期與線程相同,虛拟機棧描述的是Java方法執行的線程記憶體模型,每個方法被執行時,Java虛拟機都會同步建立一個棧桢用于存儲局部變量表,操作數棧,動态連連接配接,方法出口等資訊,每一個方法被調用直至執行完畢的過程,就對應着一個棧桢在虛拟機棧中從入棧到出棧的過程。
局部變量表中存放了編譯期可知的各種Java虛拟機基本類型(boolean byte short int char float long double)對象引用(reference)類型(它不等同于對象本身,可能是指向對象起始位址的引用指針,也可能是指向一個代表對象的句柄或者其他與此對象相關的位置),和returnAddress類型(指向了一條位元組碼指令的位址)。
這些資料類型在局部變量表中的存儲空間以局本變量槽(Shot)來表示,除了long 和double占兩個槽位,其他類型占一個槽位,局部變量表所需的記憶體空間再編譯期間完成配置設定,當進入一個方法時,這個方法需要在棧桢總配置設定多大的局部空間變量是完全确定的,在方法運作期間不會改變局部變量表的大小。
注:要執行一個方法,将該方法的棧幀壓入棧頂,方法執行完成其棧幀出棧,一個線程對應一個棧,一個棧中包含多個棧桢。
3.本地方法棧
本地方法棧與虛拟機棧發揮的作用相似,其差別是虛拟機棧為虛拟機執行Java方法(也就是位元組碼)服務,而本地方法棧則是為虛拟機使用到本地方法服務。
4.Java堆
Java堆是是虛拟機所管理的記憶體中最大的一塊,是被所有線程所共享的一塊記憶體區域,在虛拟機啟動時建立。在此記憶體區域的唯一目的就是存放對象執行個體,Java中“幾乎”所有的對象執行個體都在這裡配置設定記憶體。
Java堆既可以被實作成固定大小,也可以是可擴充的(通過參數-Xmx和-Xms設定),如果在Java堆中沒有記憶體完成執行個體配置設定,并且對也無法再擴充時,将抛出OOM異常。
5.方法區
方法區(Method Area)與Java堆一樣,是各個線程共享的記憶體區域,它用于存儲已被虛拟機加載的類型資訊、常量、靜态變量、即時編譯器後的代碼緩存等資料。
方法區的垃圾回收主要是針對常量池的回收和對類型的解除安裝,如果方法去無法滿足新的記憶體配置設定需求時,将抛出OOM異常。
6.運作時常量池
運作時常量池是方法區的一部分,Class檔案中除了有類的版本、字段、方法、接口等描述資訊外,還有一項資訊是常量池表,用于存放編譯期生成的各種字面量與符号引用,這部分内容在類加載後存放到方法去的運作時常量池中。
一般來講,除了儲存Class檔案中描述的符号引用外,還會把符号引用翻譯出來的額直接引用也存儲再運作時常量池中。
運作時常量池相對于Class檔案常量池的另外一個重要特征就是具備動态性,Java語言并不要求一個常量一定在編譯期間才能産生,運作期間可以将新的常量放入池中,利用的比較多的就是String類的intern()方法。
運作時常量池是方法區的一部分,當常量池無法再申請到記憶體時,會抛出OOM異常。
7.直接記憶體
直接記憶體并不是虛拟機運作時資料區的一部分,但這塊區域被頻繁的使用。在JDK1.4中新加入了NIO類,引入了一中基于通道與緩沖區的I/O方式,它可以使用Native函數庫直接配置設定堆外記憶體,然後通過一個存儲再Java堆裡面的DirectByteBuffer對象作為這塊記憶體的引用直接操作,這樣做能顯著提高性能,避免Java堆和Native堆中來回複制資料。當虛拟機各個記憶體區域總和大于實體限制時,同樣會出現OOM異常。