隨着時間的推移,我覺得有必要將一些之前相對模糊但是對自身技術提高會有幫助的原理、概念、實現進行一下系統的整理,所以就從JVM系列開始吧。
本系列主要參考《Java虛擬機規範(第二版)》、周志明先生寫的《深入理解Java虛擬機》,具體虛擬機主要參照Hotspot。
一.Runtime Data Areas
在運營及維護我們的站點或者應用時,需要針對crash或者內存溢出、內存泄露定位問題以及調優等工作,而Java這門語言或者是平臺提供了一個可以將內存分配、回收屏蔽掉的JVM,所以我們非常有必要了解一下JVM的運行時數據分區情況。
圖1-Runtime Data Areas
從上圖可知,我們每創建一個Tread,JVM都會爲該線程分配一個Program Counter Register、一個Java Virtual Machine Stack、一個Native Method Stack,以上三個區域都是線程私有,而Heap與Method Area是線程共享。
我們分別瞭解一下每個區域的作用、存儲的數據、線程私有\共享情況、可能出現的錯誤或異常。
1.Program Counter Register(程序計數器)
a.作用:被字節碼解釋器用來選取下一條需要執行的字節碼指令。我們需要明確一點,Java虛擬機的多線程機制是通過線程輪流切換並分配處理器執行時間的方式實現的,即在某一個特定的時刻,一個處理器只會執行一條線程中的指令,從這個層面,該區域需要線程私有。
b.存儲的數據:以當前線程正在被執行的是Java方法還是Native方法。
(1).Java方法:本線程要被執行的下一條字節碼指令地址。
(2).Native方法:空(Undefined)。
c.線程私有/共享情況:線程私有,每一個線程都持有一個程序計數器。
d.錯誤或異常:虛擬機規範中未規定該區域會拋出任何異常或錯誤。
2.Java Virtual Machine Stack(虛擬機棧)
a.作用:從名字上就可以猜測,這是一個Stack。該區域描述的是Java方法執行的內存模型:
每個Java方法執行被執行時都會常見一個棧幀(Stack Frame),該Stack Frame存儲了方法的局部變量表、操作棧、動態鏈接、方法出口等信息。每一個Java方法從被調用到執行完成都對應着一個Stack Frame從進Stack到出Stack的過程。
b.存儲的數據:本區域就是一個Stack結構,存儲的就是一個個被執行的Stack Frame.
c.線程私有/共享情況:線程私有。
d.錯誤或異常:兩種異常(更精確的說是Error)
(1).StackOverflowError:請求深度超過JVM允許的深度時拋出,簡單點理解就是這個Stack滿了而已。
(2).OutOfMemoryError:無法申請到足夠的內存時拋出。
4.Native Method Stack(本地方法棧)
這個區域的結構及作用與虛擬機棧類似,只是這個區域描述的是Native方法執行的內存模型,所以HotSpot乾脆把Java Virtual Machine Stack與Native Method Stack合併了。這個區域也是線程私有的,也會拋出StackOverflowError與OutOfMemoryError.
5.Java Heap(Java堆)
a.作用:存放對象實例(包括對象實例與數組)。
b.存儲的數據:對象實例。
c.線程私有/共享情況:線程共享。
d.錯誤或異常:OutOfMemoryError.
該區域是垃圾收集器管理的主要區域,所以爲了更快的分配內存、更好的進行垃圾回收,Java Heap還可以進行細分:新生代與老年代。如果在細分的話有新生代又由Eden空間、From Survivor空間、To Survivor空間組成。
6.Method Area(方法區)
a.作用:存儲被虛擬機加載的類信息、常量、靜態變量、JIT編譯後的代碼。
b.存儲的數據:同上
c.線程私有/共享情況:線程共享
d.錯誤或異常:OutOfMemoryError.
7.Runtime Constant Pool(運行時常量池)
a.作用:存儲編譯期生成的各種字面量和符號引用
b.存儲的數據:同上
c.線程私有/共享情況:線程共享
d.錯誤或異常:OutOfMemoryError.
該區域是Method Area的一部分。
二.Some Options
JVM Options:
參數名 | 含義 | 默認值 | 說明 |
-Xms | Java Heap初始大小 | 物理內存的1/64,但是小於1GB | |
-Xmx | Java Heap最大值 | 物理內存的1/4,但是小於1GB | |
-Xmn | 新生代大小 | 該區域由Eden空間、From Survivor空間、To Survivor空間組成 | |
PermSize | 方法區初始大小 | 物理內存的1/64 | |
MaxPermSize | 方法區的最大值 | 物理內存的1/4 | |
-Xss | JVM Stack大小 | JDK5.0之前默認256K,之後默認1M. 在物理內存不變的情況下,可以通過減小該值來獲取更大的線程創建數量。 | |
NewRatio | 新生代與老年代的比值 | 如果-XX:NewRatio=4表示新生代與老年代的比值爲1/4 | |
SurvivorRatio | Eden空間與Survivor空間的比值 | ||
LargePageSizeInBytes | 內存頁的大小 | 用法-XX:LargePageSizeInBytes=128m | |
UseFastAccessorMethods | 原始類型的快速優化 | ||
DisableExplicitGC | 忽略來自System.gc()方法觸發的垃圾收集 | 默認關閉 | |
Help Options
-XX:+PrintGC | 輸出形式: [GC 118250K->113543K(130112K), 0.0094143 secs] | ||
-XX:+PrintGCDetails | 輸出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] | ||
-XX:+PrintGCTimeStamps | |||
-XX:+PrintGC:PrintGCTimeStamps | 可與-XX:+PrintGC -XX:+PrintGCDetails混合使用 輸出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs] | ||
-XX:+PrintGCApplicationStoppedTime | 打印垃圾回收期間程序暫停的時間.可與上面混合使用 | 輸出形式:Total time for which application threads were stopped: 0.0468229 seconds | |
-XX:+PrintGCApplicationConcurrentTime | 打印每次垃圾回收前,程序未中斷的執行時間.可與上面混合使用 | 輸出形式:Application time: 0.5291524 seconds | |
-XX:+PrintHeapAtGC | 打印GC前後的詳細堆棧信息 | ||
-Xloggc:filename | 把相關日誌信息記錄到文件以便分析. 與上面幾個配合使用 | ||
-XX:+PrintClassHistogram | garbage collects before printing the histogram. | ||
-XX:+PrintTLAB | 查看TLAB空間的使用情況 | ||
XX:+PrintTenuringDistribution | 查看每次minor GC後新的存活週期的閾值 | Desired survivor size 1048576 bytes, new threshold 7 (max 15) |