Java內存區域
根據《Java虛擬機規範 第2版》的規定,Java虛擬機所管理的內存包括以下幾個運行時數據區:程序計數器,Java虛擬機棧,本地方法棧,Java堆,方法區。程序計數器,Java虛擬機棧,本地方法棧是線程私有,Java堆,方法區是線程共享。
1 程序計數器
程序計數器是一塊較小的內存區域,它的作用可以看做是當前線程所執行的字節碼的行號指示器。
在虛擬機的概念模型裏,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分 支,循環,跳轉,異常處理,線程回覆等基礎功能都要依賴這個計數器來完成。
此內存區域是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。
2 Java虛擬機棧
虛擬機棧描述的是Java方法執行的內存模型:每個方法被執行的時候都會同時創建一個棧幀用於存儲局部變量表,操作棧,動態鏈接,方法出口等信息。每一個方法被調用直至執行完成,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。
請求棧深度過大將拋出StackOverflowError異常,擴展時無法申請到足夠的內存將拋出OutOfMemoryError異常。
棧幀是用於支持虛擬機進行方法調用和方法執行的數據結構,一個棧幀需要分配多少內存,不會受到程序運行期變量數據的影響。
2.1 局部變量表
局部變量表存放編譯期可知的各種基本數據類型(boolean,byte,char,short,int,long,float,double),對象引用(reference類型,與對象位置相關)和returnAddress類型(指向一條字節碼指令的指針)。
虛擬機通過索引的方式使用局部變量表。在方法執行時,虛擬機是使用局部變量表完成參數值到參數變量列表的傳遞過程的。
2.2 操作數棧
虛擬機的解釋執行引擎稱爲“基於棧的執行引擎”,這裏的“棧”就是操作數棧
2.3 動態鏈接
每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有該引用是爲了支持方法調用過程中的動態引用
2.4 方法放回地址
方法正常退出時,調用者的PC計數器的值就是可以作爲返回地址,棧幀中很可能就會保存這個計數器值,而方法異常退出時,返回地址是要通過異常處理器表來確定,棧幀一般不會這部分信息。
方法退出的過程實際上等同於把當前棧出棧,因此退出時可能執行的操作有:回覆上層方法的局部變量表和操作數棧,把返回值壓入調用者的棧幀的操作數棧中,調用PC計數器的值以指向方法調用指令後面的一條指令。
3 本地方法棧
本地方法棧爲虛擬機執行Native方法服務,虛擬機規範對本地方法棧中的方法使用的語言,使用方式與數據結構並沒有強制規定。甚至有的虛擬機(Sun HotSpot虛擬機)直接把本地方法棧和虛擬機站合二爲一。
與虛擬機棧一樣,本地方法棧也會拋出StackOverflowError異常和OutOfMemoryError異常。
4 Java堆
Java虛擬機規範中描述的是:所有的對象實例以及數組都要在堆上分配。Java虛擬機規範的規定,Java堆可以處於物理上不連續的內存空間中,只要邏輯上是連續的即可。
從內存回收角度看,Java堆可以分爲:新生代,老年代,永久代(在Sun HotSpot JVM中這個區域將會被放棄)
新生代:Eden,Form Survivor,To Survivor(在Sun HotSpot JVM中三個區域的比例分配爲8:1:1)
5 方法區
用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據。Java虛擬機把方法區描述爲堆的一個邏輯部分。
垃圾收集在這個區域是比較少出現的,但並不是數據進入方法區就如同永久代的名字一樣“永久”存在了。這個區域的內存回收主要是針對常量池的回收和對類型卸載,一般來說這個區域的回收“成績”比較難以令人滿意,尤其是類型的卸載,條件相當苛刻,但是這部分區域的回收確實是有必要的。
運行時常量池
運行時常量池是方法區的一部分,類加載後存放編譯器Class文件中的各種字面量和符號引用。
運行期間也可以將新的常量放入常量池中,如String的intern()方法.