一、Java運行時數據區域
Java虛擬機在執行Java程序的過程中會將它所管理的內存區域劃分爲一下幾個區域:
1、程序計數器
程序計數器是一塊較小的內存空寂按,可以看作是當前線程的字節碼的行號指示器。在虛擬機的概念模型中(不同虛擬機有不同的實現方式),字節碼解釋器就是通過改變程序計數器的值來選取下一條需要執行的字節碼指令。
在多線程中,每個單獨的線程都有自己獨立的程序計數器,各個線程之間的程序計數器互不影響,我們稱這類內存區域爲“線程私有”內存區域。
如果線程執行的是一個Java方法,計數器記錄的是正在執行的虛擬機字節碼指令地址;如果當前線程執行的是Native方法,計數器的值則爲空。此內存區域是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。
2、Java虛擬機棧
與程序計數器一樣,Java虛擬機棧也是線程私有的。他的生命週期與程序計數器相同。
Java虛擬機棧描述的是java方法執行的內存模型:每個方法在執行的時候都會創建一個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。
局部變量表存放個了編譯時可知的各種基本數據類型:boolean、byte、char、short、int、float、long、double、對象引用類型(可能是一個指向對象起始地址的引用指針,也可能是一個代表對象的句柄或其他與此對象相關的位置)和return Address類型(指向了一條字節碼指令的地址)。
在Java虛擬機規範中對這塊區域定義了兩個異常:如果線程請求的棧深度大於虛擬機可提供的棧深度,將拋出StackOverflowError一場;如果虛擬機可以動態擴展(當前大部分虛擬機都可以動態擴展,只不過Java虛擬機規範中允許固定長度的虛擬機棧),如果擴展是無法申請到更多的內存空間,就會拋出OutOfMemoryError異常。
3、本地方法棧
本地方法棧和虛擬機棧作用相似,區別是虛擬機棧爲虛擬機執行Java方法服務(Java字節碼文件),而本地方法棧則爲虛擬機使用到的本地方法(Native方法)服務。有的虛擬機(如Sun HotSpot虛擬機)直接將這兩個內存區域合二爲一。
4、Java堆
Java堆是被所有線程共享的一塊內存區域,在虛擬機啓動時創建。它的唯一目的就是存放所有對象的實例。
Java堆也是垃圾回收器的主要管理區域。
由於現在垃圾回收器基本採用的是分代收集算法,所以Java堆也被分作:新生代和老年代。
根據Java虛擬機規範規定,Java堆可以處於物理上不連續的內存空間,邏輯上連續即可。
5、方法區
方法區與Java堆一樣,是一塊被所有線程共享的內存區域。它用於存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
習慣在HotSpot虛擬機開發和部署程序的開發者,很多人願意把方法區成爲永久代,本質上兩者並不等價,而是因爲HotSpot的設計團隊選擇把GC分代回收擴展到了方法區,或者說用永久代來實現方法區。對於其他虛擬機則不存在永久代的概念。
6、運行時常量池
運行時常量池是方法區的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息,還有一項是常量池。常量池主要用於存放在編譯期產生的各種字面量和符號引用,這些內容會在類加載後存放在方法區的常量池中。
運行時常量池除了保存Class文件中描述的符號引用,還會存放翻譯出來的直接引用。
運行時常量池在運行期間可以將新的常量放入運行時常量池中,可以使用String類的intern()方法,不過一般使用比較少。
7、直接內存
本機內存不是Java虛擬機的運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域,可以理解爲它指的就是本機的內存除去Java虛擬機指定的上部分內存區域。
從Java JDK1.4版本開始,Java引入了一種NIO(New Input/Ouput)類,一種基於通道和緩衝區的IO方式,它可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆裏邊的DirectByteBuffer對象作爲這塊內存的引用進行操作。因此當無法申請到足夠的內存時也會報OutOfMemoryError。
二、對象訪問
在Java虛擬機中規範了引用類型(reference)指向一個存儲在Java堆中的對象,但是並沒有制定要如何通過這個引用來定位對象。因此不同的Java虛擬機廠商有不同的實現方式。使用最多的是使用句柄池或直接指針。
(1)句柄池:
reference中存儲的就是對象的句柄地址,句柄中則包含了對象實例和類型數據的具體地址信息。
(2)、直接指針:
如果使用直接指針訪問方式,Java堆對象的佈局中就必須考慮如何放置訪問類型數據的相關信息,reference中存儲的就是對象地址。
(3)、句柄池和直接指針方式優點比較:
句柄池:使用句柄池的好處是reference中存放的是穩定的句柄,當對象被移動時,只需要改變句柄中的實例數據指針,reference本身不需要修改。
直接指針:使用直接指針方式的好處是可以節省一次指針定位的時間開銷,訪問速度更快。由於Java在運行是對對象的訪問很頻繁,因此這種時間開銷也是很大的。
在Sun HotSpot虛擬機中採用的就是直接指針的訪問方式