JVM常用工具,記憶體結構,垃圾收集,鎖學習筆記(一)
JVM(JDK1.7)
檢視JVM程序&引數
JPS
列出在主機上執行的虛擬機器,語法jps [ options ] [ hostid ]
jps-m -->輸出傳遞給JVM主方法的引數
jps -v -->輸出傳遞給JVM的引數
通常以-X -D -XX等開始的識別符號
JINFO
檢視JVM程序的配置資訊(包括未顯示指定的配置引數).
1.列印所有配置資訊
jinfo 24917
2.列印通過命令列引數傳遞給JVM的配置
jinfo -flags 24917
3.列印Java系統屬性列表
jinfo -sysprops 24917
檢視JVM執行時資訊
JSTAT
檢視JVM執行時的統計資訊,包括類載入資訊\編譯資訊\GC資訊\
1.檢視類載入資訊
jstat -class -t 1958 1 1
2.檢視Java堆的垃圾收集統計資訊
jstat -gc -t 1958 1000 3
S0C | 倖存區0容量KB[下同] |
S1C | 倖存區1容量 |
S0U | 倖存區0使用量 |
S1U | 倖存區1使用量 |
EC | 新生代容量 |
EU | 新生代使用 |
OC | 老年代容量 |
OU | 老年代使用 |
PC | 持久帶容量 |
PU | 持久帶使用 |
YGC | 年輕帶GC事件次數 |
YGCT | 年輕帶GC時間 |
FGC | 全GC事件次數 |
FGCT | 全GC時間 |
GCT | 用於垃圾回收的總時間 |
3.檢視實時的垃圾收集統計摘要
jstat -gcutil 24917 1000 5
S0 | 倖存區0使用比例 |
S1 | 倖存區1使用比例 |
E | 新生代使用百分比 |
O | 老年代使用百分比 |
P | 持久代使用百分比 |
YGC | 年輕代GC事件數量 |
YGCT | 年輕帶GC時間 |
FGCT | 全GC事件次數 |
FGCT | 全GC時間 |
檢視Java堆
JMAP
列印Java對記憶體對映.
1.列印物件共享記憶體對映(記憶體地址\共享記憶體大小\共享物件檔案的絕對路徑)
jmap 24917
2.將Java堆轉儲為hprof二進位制格式到檔案,可以使用JHAT工具來檢視.
jmap -dump:format=b,file=./tomcat.hprof 24917
3. 列印Java 堆的概要資訊,GC演算法,堆配置等
jmap -heap 24917
MaxHeapSize =16848519168 (16068.0MB) -->最大堆大小
NewSize =1310720 (1.25MB) -->新生代大小
MaxNewSize =17592186044415 MB -->最大新生代大小
OldSize =5439488 (5.1875MB) -->老年代大小
NewRatio =2 -->新生代與老年代比例
SurvivorRatio =8 -->兩個倖存區與新生代的比例
PermSize =21757952 (20.75MB) -->永久代大小
MaxPermSize =85983232 (82.0MB) -->最大永久代大小
G1HeapRegionSize = 0 (0.0MB)
檢視Java棧
JSTACK
跟蹤Java棧資訊,為給定的程序或Core檔案或者遠端除錯服務列印執行緒堆疊.對於每個Java幀,輸出完整類名\方法名\位元組程式碼索引\行號等資訊.-m引數列印程式計數器,
1.列印Java堆疊資訊,64位的JVM中
jstack -J-d64 24917
2.列印Java\Native的混合堆疊資訊
jstack -m -J-d64 24917 |more
JVM引數
通常情況下啟動一個Java應用程式就會啟動JVM的虛擬機器,虛擬機器在啟動時可以通過java指令傳遞引數給JVM.
java
用來啟動一個java程式,兩種格式:
1.java [ options ] class [arguments ]
需要指定main方法所在的類名,
2.java [ options ] -jar file.jar[ arguments ]
需要指定main所在的jar,且jar中需要使用清單檔案申明main方法
Java執行時依次從以下類路徑查詢Class
1.bootstrap class path
java平臺核心class,由JVM的 bootstrap類載入器載入,路徑為{java_home}/jre/lib
2.installed extensions
基於Java擴充套件機制,擴充套件的Java核心模組,一般放在{Java_home}/jre/lib/ext
3.user class path
使用者類路徑,開發人員提供或其它第三方java程式包,通過-classpath 、-cp或者設定CLASS_PATH環境變數來引用,JVM通過放置在{java_home}/lib/tools.jar的程式來尋找和呼叫使用者級別Class.優先順序如下:
1.預設,呼叫java的當前路徑(.),class所在的當前目錄.
2.CLASSPATH環境變數指定的路徑
3.java指令的-classpath或者-cp的值
4.-jar指定的檔案
標準引數
1.-classpath ,-cp
指定類路徑,可以使用萬用字元;
2.-Dproperty=value
設定系統屬性,可在程式彙總使用System.getproperts(“”)來獲取;
3.verbose
顯示冗餘資訊
-verbose:class à顯示關於每一個類載入器的資訊
-verbose:gc à報告每一個GC事件資訊
4.-version
列印版本資訊
非標準引數
1.-X à顯示關於非標準選項的資訊
2. -Xbootclasspath:bootclasspath
指定搜尋啟動類的目錄、jar檔案、壓縮檔案.基於此可以指定內建的JRE的路徑
3.-Xbootclasspath/a:path
將指定目錄中搜索的類新增到啟動類路徑中
4.-Xbootclasspath/p:path
5.-Xnoclassgc à禁用類垃圾收集
6.-Xloggc:file à輸出GC報告到檔案
7.-Xmnsize || -XX:NewSize à設定年輕代大小
8.-Xmsn à初始記憶體大小
9.-Xmxn à最大記憶體大小
可選的執行時選項
10.-XX:MaxGCPauseMillis=n à最大GC暫停時間
11.-XX:NewSize à年輕代大小
12.-XX:ParallelGCThreads=n à並行的垃圾收集器中執行緒數
13.-XX:+PrintGCDetails -XX:+PrintGCTimeStamps à列印垃圾收集資訊和時間戳
14.-XX:+UseConcMarkSweepGC or-XX:+UseG1GC à使用CMS||G1收集器
15.-XX:+UseParallelOldGC à使用ParallOld 收集器
垃圾收集演算法
標記清除
分標記、清除兩階段,首先標記需要回收的物件,標記完成後統一進行回收.
劣勢:
1.標記、清除兩個階段的效率都不高;
2.空間回收問題,標記清除之後會產生大量不連續的記憶體區域碎片;
*****
對於效率問題後期的垃圾收集器都並行&併發為手段來降低在標記、清除階段的效率問題,如ParNew、Par Scavenge、ParOld的並行的標記和清除;
如果使用了標記清除演算法,需要處理記憶體碎片問題,CMS收集器支援在記憶體不足時執行記憶體碎片整理和執行若干次的不壓縮的FGC後執行一次壓縮的FGC來優化記憶體.
複製演算法
為解決效率問題,記憶體劃分為兩塊,每次將存活物件複製到另外一塊記憶體,每次對整個半區的記憶體進行清理,目前的商業虛擬機器大都採用這個演算法來進行新生代的垃圾回收.
劣勢:
1.將有效記憶體降低了一半,代價高;
2.不適合物件存活率較高的場景
3.如果不浪費50%的記憶體則需要記憶體擔保
4.老年代一般不能直接使用這種演算法,沒有記憶體空間為其擔保
**
一般的用法是將記憶體劃分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor.回收時將Eden和Survivor中還存活的物件一次性的複製到另一塊Survivor空間上,最後清理掉Eden和剛才用過的Survivor空間,HostSpot虛擬機器預設的Eden和Survior的大小比例是8:1,只有百分之10的記憶體會被浪費;Survior記憶體空間不足時有老年代進行記憶體分配擔保.
標記整理
基於複製演算法的缺點,且不適合老年代使用,標記整理演算法先標記,後將存活的物件向一邊移動,之後清理掉邊界以外的記憶體區域.
分代收集
當前的商業虛擬機器都採用分代收集的方法,根據物件存活週期的不同將記憶體劃分為不同的塊.Java中堆一般分為新生代和老年代。
1.新生代朝生夕死適合使用複製演算法,
2.老年代物件存活時間長,沒有額外的記憶體分配擔保,一般使用標記-清理||標記整理演算法
垃圾收集器
HostSpotJVM現有7中垃圾收集器,如下:
Serial&Serial Old
新生代採用複製演算法,老年代採用標記整理演算法
1.單執行緒,垃圾收集時停止使用者執行緒”stop the world!”
2.Client模式下預設的新生代收集器
3.使用-XX:+UseSerialGC 顯示的指定使用序列的垃圾收集器
ParNew&ParOld
新生代採用複製演算法,老年代採用標記整理演算法;為Serial收集器的多執行緒版本;
1.使用多個執行緒並行的處理垃圾回收工作,暫停所有使用者執行緒;
2.-XX:+UseParallelOldGC 指定老年代使用Parallel Old垃圾收集器
3.-XX:+UseParNewGC 強制使用 ParNew 垃圾收集器
4.-XX:ParallelGCThreads 指定垃圾收集器的執行緒數量,預設等於CPU數量
5.只有ParalNew能與CMS配合工作;
Parallel Scavenge
於吞吐量密切相關,成為吞吐量收集器,與ParalNew相似,演算法都一致;
1.Parallel Scavenge的目標是達到一個可控制的吞吐量,減少垃圾收集的時間,讓使用者程式碼獲得更長的執行時間.
2.-XX:MaxGCPauseMillis à設定最大垃圾收集停頓時間;
3.-XX:GCTimeRatio à垃圾收集時間佔總時間的比率;
4.-XX:+UseAdptiveSizePolicy à根據當前系統執行情況收集效能監控引數,動態調整各分代中的記憶體大小和相關引數.
CMS
CMS是老年代的垃圾收集器,使用標記清理演算法,是一個併發的低停頓的收集器,
1.老年代收集器,併發清理容易帶來記憶體碎片問題,以獲得最短的停頓時間為目標,
2.併發收集,低停頓,適合於注重響應素的的場景,
3.-XX:+UseConcMarkSweepGC 指定使用CMS收集器
初始標記(stop the word) à 併發標記(與使用者執行緒併發執行)à重新標記(並行標記)à併發清除à
1. 對CPU資源非常敏感,預設的收集執行緒數量為(cpu數+3)/4
2. 無法處理浮動垃圾,可能會出現失敗,併發清除過程中使用者執行緒產生的新垃圾,
3. 標記-清除演算法產生的記憶體碎片,無法找到連續記憶體時提前觸發一次Full GC,可使用-XX:+UseCMSCompactAtFullCollection 指定開啟記憶體碎片的合併整理過程,該過程無法併發,預設開啟但是不執行.
4. -XX:+CMSFullGCsBeforeCompaction配置執行多少次不壓縮的Full GC後執行一次壓縮清理.預設為0
5. 與Parallel 相比,CMS減少了執行老年代垃圾收集時應用的暫停時間,降低了吞吐量且需要佔用更大的堆空間.
G1
Garbage-First
1. 並行&併發,並行清理,與使用者執行緒併發執行;
2. 分代收集,收集範圍包括新生代和老年代,堆記憶體佈局有很大差別,將整個堆劃分為多個大小相等的獨立區域(Region),
3. 結合多種垃圾收集演算法,空間整合,不產生碎片,整體看基於標記整理演算法,區域性看屬於複製演算法,
4. 可預測的停頓時間,可以明確指定M時間內垃圾收集消耗的時間不超過N時間
5. 面向服務端,針對有大記憶體\多處理器的機器,低GC延遲,
6. -XX:+UseG1GC 配置使用G1收集器,-XX:G1HeapRegionSize 設定每個Region的大小
初始標記(stop the word)à併發標記à最終標記(stop the world)à帥選回收
Java記憶體區域
執行緒私有
1. 程式計數器 -->JVM規範中唯一沒有規定記憶體溢位的區域.
2. 虛擬機器棧 -->超出棧深度報棧溢位異常,請求記憶體不足報記憶體溢位.
Java棧是方法執行的記憶體模型,Java棧中儲存一個個幀棧,每個幀棧對應一個被呼叫的方法,幀棧結構包括(區域性變量表\運算元棧\指向當前方法所屬類的執行時常量池的引用\方法返回地址等);一個方法的執行伴隨一個幀棧的建立\壓棧和出棧操作.
每個執行緒都會有自己的幀棧互不干擾;
3. 本地方法棧 -->為執行本地方法服務;
共享區域
1. Java堆 -->用來儲存物件&陣列的區域
被所有執行緒共享,垃圾收集的主要區域;
2. 方法區 -->儲存每個類的靜態資訊
所有執行緒共享,儲存了每個類的資訊(類名\方法資訊\欄位資訊)、靜態變數、常量以及編譯器編譯後的程式碼等.
方法區中包含執行時常量,每一個類或介面的常量池的執行時表示形勢
Java加鎖機制
Java中的執行緒安全
1. 不可變
2. 絕對執行緒安全,通常所說的執行緒安全的容器並不是絕對執行緒安全的
3. 相對執行緒安全,一般的同步容器為相對執行緒安全的
4. 執行緒相容,物件本身非執行緒安全,可以正確的使用同步手段使其可以正確的併發使用
5. 執行緒對立,無論是否採用正確的同步都無法併發的使用
執行緒安全的實現方法
1. 互斥同步,互斥是因,同步是果,常見的Synchronized、ReentrankLock(可重入鎖) ;進行執行緒阻塞和喚醒帶來的效能問題à悲觀併發控制.
2. 非阻塞同步,先操作在判斷是否有衝突,常見的Atomic*型別的實現,通常情況下需要作業系統指令的支援(測試並設定\獲取並增加\交換\比較並交換等語意),非阻塞同步的許多實現不需要將執行緒掛起所以成為非阻塞同步à樂觀併發控制
3. 無同步方案,不涉及資料共享,可重入程式碼\執行緒本地儲存
鎖優化
1. 自旋鎖&自適應自旋鎖
2. 鎖消除
3. 鎖粗化
4. 輕量級鎖
5. 偏向鎖
使用-XX:+UseBiasedLocking使用偏向鎖
鎖獲取&鎖釋放
1. 無鎖狀態是最佳的 à不需要獲取鎖
2. 首先獲取偏向鎖 à出現競爭時升級為輕量級鎖,同時釋放鎖
3. 輕量級鎖,CAS加鎖 à兩個以上執行緒競爭鎖,升級為重量級鎖
4. 重量級鎖,執行緒被阻塞,直到鎖釋放喚醒執行緒.
JVM(jdk1.8)
Java
差別還是挺大的,指令的名稱有變化,記起來還是比較麻煩的,大概知道有哪些,用的時候man java查以下.
常見的要知道:
1. 如何為java程式指定不同的類路徑
2. 如何調整JVM的執行時記憶體引數
3. 如何檢視JVM執行時的各種統計狀態,核心的如GC狀態\GC日誌\執行緒狀態
4. 如何配置並選擇合適的GC收集器
5. Java記憶體模型和鎖的關係
6. Java中鎖的種類,以及不同型別的實現[併發程式設計],鎖優化