運行時數據區
- 程序計數器(線程私有)
- 一塊較小的內存空間,可以看作是當前線程所執行的字節碼的行號指示器。如果線程正在執行的是一個java方法,記錄的是正在執行的虛擬機字節碼指令的地址;如果執行的是native方法,這個計數器值則爲空。注意:此內存區域是唯一 一個在java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域
- java虛擬機棧(線程私有)
- 虛擬機棧描述的是java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀(stack frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。
- 局部變量表存放了編譯期可知的各種基本數據類型、對象引用和returnAddress類型(指向了一條字節碼指令的地址) 注意:其中64位長度的long和double類型的數據會佔用2個局部變量空間(slot),其餘數據只佔用一個。
- 棧區域的兩種異常:StackOverflowError 和 OutOfMemoryError異常
- 本地方法棧(線程私有)
- 本地方法棧是虛擬機使用到的native方法服務
- java堆
- 堆是虛擬機管理的內存中最大的一塊,java堆是被所有線程共享的一塊內存區域,在虛擬機啓動時創建
- java堆細分:新生代和老年代。細緻一點Eden空間、from survivor空間、To survivor空間等。從內存分配的角度來看,線程共享的java堆中可能劃分出多個線程私有的分配緩衝區(Thread Local Allocation Buffer,TLAB)
- -Xmx和-Xms設置虛擬機堆內存是否可擴展
- 方法區(別名Non-Heap 非堆)
- 用於存儲已被虛擬機加載的類信息、常量、靜態變量、及時編譯器編譯後的代碼等數據表。
- 運行時常量池是方法區的一部分。class文件中除了有類的版本、字段、方法、接口等描述信息,還有一項信息是常量池,用於存放編譯器生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區的運行時常量池中存放。
- 直接內存
- 在jdk1.4中新加入了nio類,引入了一種基於通道與緩衝區的i/o方式,它可以使用native函數庫直接分配堆外內存,然後通過一個存儲在java堆中directByteBuffer對象作爲這塊內存的引用進行操作。這樣可以在一些場景中顯著提高性能,因爲避免了在java堆和native堆中來回複製數據。
- 本機直接內存的分配不受java堆大小的限制,但是如果在設置 -Xmx等參數信息時,忽略掉直接內存,可能導致總的內存大於了物理內存限制,從而導致動態擴展時出現OutOfMemoryError異常。
幾種常見的OutOfMemoryError
- java堆溢出
- java.lang.OutOfMemoryError : java heap space
- 當出現堆溢出時一般手段是先通過內存映像分析工具(如:eclipse memory analyzer)對dump出來的堆轉存快照進行分析。首先要清楚是內存泄漏還是內存溢出
- 虛擬機棧和本地方法棧溢出
- 棧容量只由 -Xss 參數設定
- 關於虛擬機棧和本地方法棧,在java虛擬機規範中描述了兩種異常:
- 如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出StackOverflowerror
- 如果虛擬機在擴展棧時無法申請到足夠的空間,則拋出OutOfMemoryError
- 方法區和運行時常量池溢出
- VM Args:-XX:PermSize = 10M -XX:MaxPermSize=10M
- java.lang.OutOfMemoryError : PermGen space
- 本機直接內存溢出
- DirectMemory容量可以通過-XX : MaxDirectMemorySize指定,如果不指定,則默認與java堆最大值一樣
- 由directMemory導致的內存溢出,一個明顯的特徵是在Heap Dump文件中不會看見明顯的異常,如果發現OOM之後dump文件很小,而程序中又直接或間接使用了NIO,則可以考慮是不是這方面問題
內存分配和回收策略
對象優先在Eden分配
大多數情況,對象在新生代Eden去分配。當Eden區沒有足夠空間進行分配時,虛擬機將發起一次Minor GC。
大對象直接進入老年代
所謂大對象是指—需要大量連續內存空間的java對象(最典型的大對象就是那種很長的字符串以及數組)。虛擬機提供了一個 -XX:PretenureSizeThreshold參數,令大於這個設置值的對象直接在老年代分配。這樣做的目的是避免在Eden區及兩個survivor區之間發生大量的內存複製。
長期存活的對象將進入老年代
在Eden中新生的對象,經歷第一次Minor GC後存在,且能被survivor容納,將被移動到survivor中,並年齡設爲1。每經過一次,年齡+1,直到達到閾值(默認15,可以通過-XX:MaxTenuringThreshold設置)
動態年齡判斷
如果survivor空間中相同年齡所有對象大小總和大於survivor空間的一半,年齡大於或者等於該年齡的對象直接進入老年代,無需等年齡到達閾值。
垃圾收集算法
- 標記-清除算法
- 複製算法
- 標記-整理算法
- 分代收集算法
虛擬機性能監控與故障處理工具
jdk的命令行工具
- jps:虛擬機進程狀況工具
- 可以列出正在運行的虛擬機進程,並顯示虛擬機執行主類(Main Class,main()函數所在的類)名稱以及這些進程的本地虛擬機唯一id(Local Virtual Machine Identifier , LVMID)
- -q : 只輸出LVMID,省略主類的名稱
- -m : 輸出虛擬機進程啓動時傳遞給主類main()函數的參數
- -l : 輸出主類的全名,如果進程執行的是jar包,輸出jar的路徑
- -v : 輸出虛擬機啓動時jvm參數
- jstat:虛擬機統計信息監視工具
- JVM Statistics Monitoring Tool是用於監視虛擬機各種運行狀態信息的命令行工具,他可以顯示本地或者遠程虛擬機進程中的類轉載、內存、垃圾收集、jit編譯等運行數據,在服務器上它是運行期定位虛擬機性能問題的首選工具
- -gc : 監視java堆,包括Eden區,兩個survivor區,老年代、永久代等的容量、已用空間、gc時間合計等信息
- -class : 監視類裝載、卸載數量、總空間以及類裝載所耗費的時間
- -gcutil : 監視內容與-gc基本相同,但輸出主要關注已使用空間佔總空間的百分比
- jinfo:java配置信息工具
- Configuration Info for Java 的作用是實時地查看和調整虛擬機各項參數。
- jmap:java內存映射工具
- Memory Map for Java命令用於生成堆轉儲快照(一般稱爲heapdump或dump文件)。
- -dump:生成java堆轉儲快照
- -dump:[live,]format=b,file= live子參數說明是否只dump出存活的對象
- -heap:顯示java堆詳細信息,如使用哪種回收器、參數配置、分代狀況等
- -histo:顯示堆中對象統計信息,包括類、實例數量、合計容量
- -F:當虛擬機進程對-dump選項沒有響應時,可使用這個選項強制生成dump快照。
- jhat:虛擬機堆轉儲快照分析工具
- sun JDK提供了jhat(JVM Heap Analysis Tool)命令與jmap搭配使用,來分析生成的堆轉儲快照。jhat內置了一個微型的HTTP/HTML服務器,生成dump文件的分析結果後,可以在瀏覽器查看。
- 注意:一般不要在生產服務器上用該命令分析dump文件
- 分析工具是一個耗時而且消耗硬件資源的過程
- jhat的分析功能相對來說比較簡陋
- jstack:java堆棧跟蹤工具
- Stack Trace for Java 命令用於生成虛擬機當前時刻的線程快照(一般稱爲threaddump或者Javacore文件)
- -l :除堆棧外,顯示關於鎖的附加信息
- -m :如果調用本地方法的話,可以顯示C/C++的堆棧
- -F :當正常輸出的請求不被響應時,強制輸出線程堆棧