一、JVM模型概述
java虛擬機(JVM)在java程序運行的過程中,會將它所管理的內存划分為若干個不同的數據區域,這些區域有的隨着JVM的啟動而創建,有的隨着用戶線程的啟動和結束而建立和銷毀。一個基本的JVM運行時內存模型如下所示:

上圖展示的是“JAVA SE7”的JVM虛擬機規范。注意,虛擬機規范並不是一成不變的,Oracle在發布新的JAVA版本時,可能會對JVM做一定的優化和改進,例如在JDK8的版本中,方法區被移除,取而代之的是metaspace(元數據空間)。
在本章及下面的章節中,將以JDK7的標准作為例子,對JVM的運行時數據區進行講解。
二、程序計數器(Program Counter Register)
2.1)什么是程序計數器
程序計數器是一個記錄着當前線程所執行的字節碼的行號指示器。
JAVA代碼編譯后的字節碼在未經過JIT(實時編譯器)編譯前,其執行方式是通過“字節碼解釋器”進行解釋執行。簡單的工作原理為解釋器讀取裝載入內存的字節碼,按照順序讀取字節碼指令。讀取一個指令后,將該指令“翻譯”成固定的操作,並根據這些操作進行分支、循環、跳轉等流程。
從上面的描述中,可能會產生程序計數器是否是多余的疑問。因為沿着指令的順序執行下去,即使是分支跳轉這樣的流程,跳轉到指定的指令處按順序繼續執行是完全能夠保證程序的執行順序的。假設程序永遠只有一個線程,這個疑問沒有任何問題,也就是說並不需要程序計數器。但實際上程序是通過多個線程協同合作執行的。
首先我們要搞清楚JVM的多線程實現方式。JVM的多線程是通過CPU時間片輪轉(即線程輪流切換並分配處理器執行時間)算法來實現的。也就是說,某個線程在執行過程中可能會因為時間片耗盡而被掛起,而另一個線程獲取到時間片開始執行。當被掛起的線程重新獲取到時間片的時候,它要想從被掛起的地方繼續執行,就必須知道它上次執行到哪個位置,在JVM中,通過程序計數器來記錄某個線程的字節碼執行位置。因此,程序計數器是具備線程隔離的特性,也就是說,每個線程工作時都有屬於自己的獨立計數器。
2.2)程序計數器的特點
1.線程隔離性,每個線程工作時都有屬於自己的獨立計數器。
2.執行java方法時,程序計數器是有值的,且記錄的是正在執行的字節碼指令的地址(參考上一小節的描述)。
3.執行native本地方法時,程序計數器的值為空(Undefined)。因為native方法是java通過JNI直接調用本地C/C++庫,可以近似的認為native方法相當於C/C++暴露給java的一個接口,java通過調用這個接口從而調用到C/C++方法。由於該方法是通過C/C++而不是java進行實現。那么自然無法產生相應的字節碼,並且C/C++執行時的內存分配是由自己語言決定的,而不是由JVM決定的。
2.執行java方法時,程序計數器是有值的,且記錄的是正在執行的字節碼指令的地址(參考上一小節的描述)。
3.執行native本地方法時,程序計數器的值為空(Undefined)。因為native方法是java通過JNI直接調用本地C/C++庫,可以近似的認為native方法相當於C/C++暴露給java的一個接口,java通過調用這個接口從而調用到C/C++方法。由於該方法是通過C/C++而不是java進行實現。那么自然無法產生相應的字節碼,並且C/C++執行時的內存分配是由自己語言決定的,而不是由JVM決定的。


4.程序計數器占用內存很小,在進行JVM內存計算時,可以忽略不計。
5.程序計數器,是唯一一個在java虛擬機規范中沒有規定任何OutOfMemoryError的區域。