JVM快速調優手冊之一: 記憶體結構(堆記憶體和非堆記憶體)
圖為Java虛擬機器執行時的資料區:
1.方法區
也稱"永久代” 、“非堆”, 它用於儲存虛擬機器載入的類資訊、常量、靜態變數、是各個執行緒共享的記憶體區域。預設最小值為16MB,最大值為64MB(未驗證),可以通過-XX:PermSize 和 -XX:MaxPermSize 引數限制方法區的大小。
執行時常量池:是方法區的一部分,Class檔案中除了有類的版本、欄位、方法、介面等描述資訊外,還有一項資訊是常量池,用於存放編譯器生成的各種符號引用,這部分內容將在類載入後放到方法區的執行時常量池中。
2.虛擬機器棧
描述的是java 方法執行的記憶體模型:每個方法被執行的時候 都會建立一個“棧幀”用於儲存區域性變量表(包括引數)、操作棧、方法出口等資訊。每個方法被呼叫到執行完的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。宣告週期與執行緒相同,是執行緒私有的。
區域性變量表存放了編譯器可知的各種基本資料型別(boolean、byte、char、short、int、float、long、 double)、物件引用(引用指標,並非物件本身),其中64位長度的long和double型別的資料會佔用2個區域性變數的空間,其餘資料型別只佔1 個。區域性變量表所需的記憶體空間在編譯期間完成分配,當進入一個方法時,這個方法需要在棧幀中分配多大的區域性變數是完全確定的,在執行期間棧幀不會改變區域性 變量表的大小空間。
3.本地方法棧
與虛擬機器棧基本類似,區別在於虛擬機器棧為虛擬機器執行的java方法服務,而本地方法棧則是為Native方法服務。
4.堆
也叫做java 堆、GC堆,是java虛擬機器所管理的記憶體中最大的一塊記憶體區域,也是被各個執行緒共享的記憶體區域,在JVM啟動時建立。該記憶體區域存放了物件例項及陣列(所有new的物件)。其大小通過-Xms(最小值)和-Xmx(最大值)引數設定,-Xms為JVM啟動時申請的最小記憶體,-Xmx為JVM可申請的最大記憶體。在JVM啟動時,最大記憶體會被保留下來。為物件記憶體而保留的地址空間可以被分成年輕代和老年代。
預設當空餘堆記憶體小於40%時,JVM會增大Heap到-Xmx指定的大小,可通過-XX:MinHeapFreeRation=來指定這個比列;當空餘堆記憶體大於70%時,JVM會減小heap的大小到-Xms指定的大小,可通過XX:MaxHeapFreeRation=來指定這個比列,對於執行系統,為避免在執行時頻繁調整Heap的大小,通常-Xms與-Xmx的值設成一樣。
Parameter | Default Value |
---|---|
MinHeapFreeRatio | 40 |
MaxHeapFreeRatio | 70 |
-Xms | 3670k |
-Xmx | 64m |
注:如果是64位系統,這些值一般需要擴張30%,來容納在64位系統下變大的物件。
從J2SE 1.2開始,JVM使用分代收集演算法,在不同年代的區域裡使用不同的演算法。堆被劃分為新生代和老年代。新生代主要儲存新建立的物件和尚未進入老年代的物件。老年代儲存經過多次新生代GC(MinorGC)任然存活的物件。|
注1:圖中的Perm不是堆記憶體,是永久代
注2:圖中的Virtaul則是各區域還未被分配的記憶體,即最大記憶體-當前分配的記憶體
新生代:
新生代包括一塊eden(伊甸園)和2塊survivor(通常又稱S0和S1或From和To)。大多數物件都是在eden中初始化。而對於2塊survivor來說,總有一塊是空的,它會在下一個複製收集過程中作為eden中的活躍物件和另一塊survivor的目的地。在物件衰老之前(也就是被複制到tenured之前),它們會在兩塊survivor區域之間以這樣的方式複製。可通過-Xmn引數來指定新生代的大小,也可以通過-XX:SurvivorRation來調整Eden Space及Survivor Space的大小。
老年代:
用於存放經過多次新生代Minor GC依然存活的物件,例如快取物件,新建的物件也有可能直接進入老年代,主要有兩種情況:①.大物件,可通過啟動引數設定-XX:PretenureSizeThreshold=1024(單位為位元組,預設為0)來代表超過多大時就不在新生代分配,而是直接在老年代分配。②.大的陣列物件,即陣列中無引用外部物件。
老年代所佔的記憶體大小為-Xmx對應的值減去-Xmn對應的值。
5.程式計數器
是最小的一塊記憶體區域,它的作用是當前執行緒所執行的位元組碼的行號指示器,在虛擬機器的模型裡,位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,分支、迴圈、異常處理、執行緒恢復等基礎功能都需要依賴計數器完成。