VM運行時數據區域介紹

from:http://wenku.baidu.com/view/e73e0fafdd3383c4bb4cd25b.html html

JVM執行程序的過程當中,會使用到各類數據區域,這些區域有各自的用途,建立和銷燬時間。根據《Java虛擬機規範(第二版)》(下文稱VM Spec)的規定,JVM包括下列幾個運行時數據區域: 數組

1.程序計數器(Program Counter Register) 服務器

每個Java線程都有一個程序計數器來用於保存程序執行到當前方法的哪個指令,對於非Native方法,這個區域記錄的是正在執行的VM原語的地址,若是正在執行的是Natvie方法,這個區域則爲空(undefined)。此內存區域是惟一一個在VM Spec中沒有規定任何OutOfMemoryError狀況的區域。 函數

2.Java虛擬機棧(Java Virtual Machine Stacks) 性能

與程序計數器同樣,VM棧的生命週期也是與線程相同。VM棧描述的是Java方法調用的內存模型:每一個方法被執行的時候,都會同時建立一個幀(Frame)用於存儲本地變量表、操做棧、動態連接、方法出入口等信息。每個方法的調用至完成,就意味着一個幀在VM棧中的入棧至出棧的過程。在後文中,咱們將着重討論VM棧中本地變量表部分。  優化

常常有人把Java內存簡單的區分爲堆內存(Heap)和棧內存(Stack),實際中的區域遠比這種觀點複雜,這樣劃分只是說明與變量定義密切相關的內存區域是這兩塊。其中所指的「堆」後面會專門描述,而所指的「棧」就是VM棧中各個幀的本地變量表部分。本地變量表存放了編譯期可知的各類標量類型(boolean、byte、char、short、int、float、long、double)、對象引用(不是對象自己,僅僅是一個引用指針)、方法返回地址等。其中long和double會佔用2個本地變量空間(32bit),其他佔用1個。本地變量表在進入方法時進行分配,當進入一個方法時,這個方法須要在幀中分配多大的本地變量是一件徹底肯定的事情,在方法運行期間不改變本地變量表的大小。  spa

在VM Spec中對這個區域規定了2中異常情況:若是線程請求的棧深度大於虛擬機所容許的深度,將拋出StackOverflowError異常;若是VM棧能夠動態擴展(VM Spec中容許固定長度的VM棧),當擴展時沒法申請到足夠內存則拋出OutOfMemoryError異常操作系統

3.本地方法棧(Native Method Stacks) 線程

本地方法棧與VM棧所發揮做用是相似的,只不過VM棧爲虛擬機運行VM原語服務,而本地方法棧是爲虛擬機使用到的Native方法服務。它的實現的語言、方式與結構並無強制規定,甚至有的虛擬機(譬如Sun Hotspot虛擬機)直接就把本地方法棧和VM棧合二爲一。和VM棧同樣,這個區域也會拋出StackOverflowError和OutOfMemoryError異常。  指針

4.Java堆(Java Heap)

對於絕大多數應用來講,Java堆是虛擬機管理最大的一塊內存。Java堆是被全部線程共享的在虛擬機啓動時建立。Java堆的惟一目的就是存放對象實例,絕大部分的對象實例都在這裏分配。這一點在VM Spec中的描述是:全部的實例以及數組都在堆上分配(原文:The heap is the runtime data area from which memory for all class instances and arrays is allocated),可是在逃逸分析和標量替換優化技術出現後,VM Spec的描述就顯得並不那麼準確了。

 Java堆內還有更細緻的劃分:新生代、老年代,再細緻一點的:eden、from survivor、to survivor,甚至更細粒度的本地線程分配緩衝(TLAB)等,不管對Java堆如何劃分,目的都是爲了更好的回收內存,或者更快的分配內存,在本章中咱們僅僅針對內存區域的做用進行討論,Java堆中的上述各個區域的細節,可參考《JVM內存管理:深刻垃圾收集器與內存分配策略》。 

根據VM Spec的要求,Java堆能夠處於物理上不連續的內存空間,它邏輯上是連續的便可,就像咱們的磁盤空間同樣。實現時能夠選擇實現成固定大小的,也能夠是可擴展的,不過當前全部商業的虛擬機都是按照可擴展來實現的(經過-Xmx和-Xms控制)。若是在堆中沒法分配內存,而且堆也沒法再擴展時,將會拋出OutOfMemoryError異常。

5.方法區(Method Area)

叫「方法區」可能認識它的人還不太多,若是叫永久代(Permanent Generation)它的粉絲也許就多了。它還有個別名叫作Non-Heap(非堆),可是VM Spec上則描述方法區爲堆的一個邏輯部分(原文:the method area is logically part of the heap),這個名字的問題還真容易使人產生誤解,咱們在這裏就不糾結了。 

方法區中存放了每一個Class的結構信息,包括常量池、字段描述、方法描述等等。VM Space描述中對這個區域的限制很是寬鬆,除了和Java堆同樣不須要連續的內存,也能夠選擇固定大小或者可擴展外,甚至能夠選擇不實現垃圾收集。相對來講,垃圾收集行爲在這個區域是相對比較少發生的,但並非某些描述那樣永久代不會發生GC(至少對當前主流的商業JVM實現來講是如此),這裏的GC主要是對常量池的回收和對類的卸載,雖然回收的「成績」通常也比較差強人意,尤爲是類卸載,條件至關苛刻

6.運行時常量池(Runtime Constant Pool)

Class文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量表(constant_pool table),用於存放編譯期已可知的常量,這部份內容將在類加載後進入方法區(永久代)存放。可是Java語言並不要求常量必定只有編譯期預置入Class的常量表的內容才能進入方法區常量池,運行期間也可將新內容放入常量池(最典型的String.intern()方法)。 

運行時常量池是方法區的一部分,天然受到方法區內存的限制,當常量池沒法在申請到內存時會拋出OutOfMemoryError異常。

7.本機直接內存(Direct Memory

直接內存並非虛擬機運行時數據區的一部分,它根本就是本機內存而不是VM直接管理的區域。可是這部份內存也會致使OutOfMemoryError異常出現,所以咱們放到這裏一塊兒描述。 

在JDK1.4中新加入了NIO類,引入一種基於渠道與緩衝區的I/O方式,它能夠經過本機Native函數庫直接分配本機內存,而後經過一個存儲在Java堆裏面的DirectByteBuffer對象做爲這塊內存的引用進行操做。這樣能在一些場景中顯著提升性能,由於避免了在Java對和本機堆中來回複製數據。 顯然本機直接內存的分配不會受到Java堆大小的限制,可是即然是內存那確定仍是要受到本機物理內存(包括SWAP區或者Windows虛擬內存)的限制的,通常服務器管理員配置JVM參數時,會根據實際內存設置-Xmx等參數信息,但常常忽略掉直接內存,使得各個內存區域總和大於物理內存限制(包括物理的和操做系統級的限制),而致使動態擴展時出現OutOfMemoryError異常

相關文章
相關標籤/搜索