Java虛擬機在執行Java程序的過程中會把它所管理的內存區域分為若干個不同的數據區域。這些區域都有各自的用途,有的區域隨虛擬機進程啟動而存在,有的區域則依賴用戶線程啟動和結束而建立和銷毀。Java虛擬機所管理的內存將會包括以下幾個運行時數據區域。
程序計數器
程序計數器是一塊較小的內存空間,它的作用可以看做是當前線程所執行的字節碼的行號指示器,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支,循環,跳轉,異常處理,線程恢復等基礎功能都需要依賴這個計數器來完成。
由於每個線程都有一個獨立的程序計數器,以保證虛擬機在線程切換後能恢復到正確的執行位置(使用線程私有內存,各線程之間互不影響)。
如果線程當前執行的是JAVA代碼,這個計數器記錄的時正在執行虛擬機字節碼指令的地址;如果是Native方法,這個計數器值則為空。此內存區域是唯一一個在Java虛擬機規范中沒有規定任何OutOfMemoryError情況的區域。
Java虛擬機棧
與程序計數器一樣,也是線程私有的,生命周期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法被執行的時候都會同時創建一個棧幀用於存儲局部變量表,操作棧,動態鏈接,方法出口等信息。每個方法被調用直到執行完成的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。
局部變量表存放了編譯期可知的各種基本數據類型(boolean,byte,char,short,int,float,long,double),對象引用(reference),returnAddress(指向一條字節碼指令的地址)。
long 和double類型的數據會占用2個局部變量空間(Slot) ,其他的數據類型只占一個。局部變量表所需內存在編程期間完成分配,當進入一個方法時,這個方法需要的幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。
本地方法棧
本地方法棧與虛擬機棧非常相似。區別是:虛擬機棧為虛擬機執行Java代碼(字節碼)服務,而本地方法棧則是:虛擬機使用到的Native方法服務。
Java堆
Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域唯一目的就是存放對象實例,幾乎所有對象實例以及數組都
在這裡分配內存。(但隨著JIT編譯器的發展與逃逸分析技術的逐漸成熟,棧上分配,標量替換,優化技術將會導致一些微妙的變化,所有的對象都在堆上分配變得不是那麼絕對了)
Java堆是垃圾收集管理的主要區域。Java堆可以細分為:新生代和老年代,在細致一點有:Eden空間,From Survivor空間,To Survivor空間等。如果從內存分配的角度看,線程共享的Java堆中可能劃分出多個線程私有的分配緩沖區(Thread Local Allocation Buffer ,TLAB).
方法區(永久代)
方法區和Java堆一樣,是個線程共享的內存區域,它用於存儲虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據。
運行時常量池
運行時常量池是方法區的一部分.Class 文件中除了由類的版本信息,字段,方法,接口等描述信息外,還有一項信息是 常量池(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用。