1. JVM的運行參數(shù)
1.1 三種參數(shù)類型
-
標(biāo)準(zhǔn)參數(shù)
- help
- -version
-
-X參數(shù) (非標(biāo)準(zhǔn)參數(shù))
- -Xint
- -Xcomp
-
-XX參數(shù)(使用率比較高, 常用語jvm調(diào)優(yōu))
- -XX:newSize
- -XX:+UseSeriaIGC
1.2 標(biāo)準(zhǔn)參數(shù)
-D 設(shè)置系統(tǒng)屬性: java -Dstr=hello
String str = System.getProperty("str")
println(str)
輸出結(jié)果: hello
JVM的兩個啟動模式: server和client
JVM在啟動的時候回根據(jù)硬件和操作系統(tǒng)自動選擇是使用Server還是Client類型的JVM
64位操作系統(tǒng)只有Server類型翠胰,沒有Client
1.3 非標(biāo)準(zhǔn)參數(shù)
JVM的運行模式:
-Xint: 解釋模式誉裆, 強制要求JVM執(zhí)行所有的字節(jié)碼(interpreted mode)
-Xcomp: 編譯模式渴语,JVM在第一次使用時會把所有字節(jié)碼編譯成本地代碼(compiled mode)
-Xmixed:混合模式衙传,將解釋模式和編譯模式混合使用, JVM默認的模式憨奸, 也是推薦的模式(mixed mode)
1.4 -XX 參數(shù)
-XX參數(shù)也是非標(biāo)準(zhǔn)參數(shù)驱富, 主要用于JVM的調(diào)優(yōu)和debug操作
兩種使用方式:
- boolean類型
格式: -XX:[+-]<name> 表示啟用或者禁用name指令
-XX:+DisableExplicitGC表示禁用手動調(diào)用gc操作锚赤, System.gc()無效 - 非boolean類型
格式: -XX<name>=<value> 表示name的屬性值為value
-XX:NewRatie=1 表示新生代和老年代的比值
1.5 -Xms, -Xmx
-Xms和-Xmx分別是設(shè)置jvm的堆內(nèi)存的初始大小和最大大小
-Xms512m相當(dāng)于: -XX:InitialHeapSize=512m
-Xms2048m相當(dāng)于:-XX:MaxHeapSize=2048m
JVM啟動時會自動設(shè)置Heap size的值, -Xms初始空間是物理內(nèi)存的1/64, -Xmx最大值時物理空間的1/4褐鸥。
進行JVM優(yōu)化時线脚, 可將-Xms和-Xmx設(shè)置值相同, 最大值不超過物理內(nèi)存的80%
1.6 查看jvm運行參數(shù):
運行java命令時打印參數(shù): -XX:+PrintFlagsFinal
參數(shù)列表中: = 表示默認值, :=表示值被修改過
1.7 查看正在運行的進程的jvm信息
jps -l
查看當(dāng)前系統(tǒng)中所有運行的java項目的進程id及詳細包信息
jinfo -flags 29200
查看進程id為29200進程的所有jvm參數(shù)信息
jinfo -flag MaxheapSize 29200
查看pid下的具體某一jvm信息
2. jvm的內(nèi)存模型
jvm的內(nèi)存模型1.7和1.8有較大的區(qū)別
2.1 jdk1.7的堆內(nèi)存模型:
- 年輕區(qū): 新new的一些對象會在這個區(qū)域浑侥,young區(qū)被分為:Eden區(qū)和兩個大小嚴(yán)格相同的Survivor區(qū)姊舵, 當(dāng)Eden區(qū)變滿時, 數(shù)據(jù)會移到Survior中寓落,幾次jvm垃圾收集后括丁, 依然存活的Survivor會知道老年區(qū)
- 老年區(qū):tenured區(qū)主要保存生命周期長的對象, 一般是一些老的對象
- 永久區(qū): 主要保存class, method, field對象伶选, 這部分空間一般不會溢出
2.2 jdk1.8的堆內(nèi)存模型
年輕區(qū):Eden+2*Survivor
老年區(qū):OldGen
元數(shù)據(jù)空間(Metaspace):Matespace是不是在虛擬機內(nèi)部史飞, 而是占用服務(wù)器的內(nèi)存空間,這是和1.7最大的區(qū)別
2.3 為什么廢除了1.7的永久區(qū)
現(xiàn)實中是因為永久代內(nèi)存總會發(fā)生不夠用或者內(nèi)存泄漏仰税, 基于此將永久區(qū)廢棄祸憋, 改為使用本地的內(nèi)存空間
2.4 通過jstat命令進行查看堆內(nèi)存的使用情況
jstat 參數(shù)指令 進程id 時間間隔 查詢次數(shù)
jstat -class 29200
查看類加載情況
loaded: 加載類的數(shù)量
Bytes:類占得空間
uloaded:未加載類的數(shù)量
Time: 加載占用的時間
jstat -compiler 29200
查看編譯情況
查看垃圾回收器的使用情況
jstat -gc 29200 1000 10
每1000毫秒(1秒)鐘打印一次gc使用情況, 總共打印10次
3. jmap的使用以及內(nèi)存溢出分析
3.1 查看內(nèi)存使用情況
jmap -heap 29200
3.2 查看內(nèi)存中對象數(shù)量及大行の浴:
jmap -histo <pid> | more
查看所有對象的
jmap -histo:live <pid> | more
查看活躍對象的
[B-> byte
[I -> int
[C->char
3.3 將內(nèi)存使用情況dump到文件中, jhat對快照文件分析
jmap -dump:format=b,file=filename <pid>
例如 jmap -dump:format=b,file=dumptest.dat 29200 會生成一份29200內(nèi)存使用情況的二進制文件dumptest.dat
使用jhat對dump的二進制文件分析:jhat port 9999 dumptest.dat
瀏覽器訪問7000端口:
4. 內(nèi)存溢出的定位與分析
內(nèi)存溢出在生產(chǎn)環(huán)境中經(jīng)常會遇到, 比如不斷地將數(shù)據(jù)寫入到一個集合中掸鹅,出現(xiàn)了死循環(huán)塞帐, 讀取超大文件等等,都可能造成內(nèi)存溢出巍沙。
4.1模擬內(nèi)存溢出:
設(shè)置jvm參數(shù): -Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
jvm初始內(nèi)存8m葵姥, 最大內(nèi)存8m, 內(nèi)存溢出時dump內(nèi)存快照
編寫代碼:
public static void main(String[] args) {
ArrayList<String> strings = new ArrayList<>();
for (int i = 0; i <= 1000000000; i++){
String id = "";
for (int j = 0; j < 1000; j++){
id += UUID.randomUUID().toString();
}
strings.add(id);
}
}
mat工具分析內(nèi)存
4.2 jstack的使用:
有時候我們需要查看jvm中的線程執(zhí)行情況句携, 比如發(fā)現(xiàn)CPU的負載突然增高榔幸,出現(xiàn)了死鎖,死循環(huán)等矮嫉, 由于程序正常運行的削咆,沒有任何的輸出, 從日志方面也看不出什么問題蠢笋, 需要從jvm的內(nèi)部線程的執(zhí)行情況查找并且分析原因拨齐。
jstack <pid>
可以看到當(dāng)前pid進程中所有線程的執(zhí)行情況。
5. jstack中Java中線程的狀態(tài)
- 初始狀態(tài)(NEW): 創(chuàng)建一個thread對象昨寞, 但還未調(diào)用start啟動線程瞻惋, 則處于初始狀態(tài)
- 運行狀態(tài)(RUNNABLE):
- 就緒狀態(tài): 等待CPU分配執(zhí)行權(quán), 放在就緒隊列中
- 運行狀態(tài):獲得CPU的執(zhí)行權(quán)援岩, 一個CPU在同一時間只能執(zhí)行一個線程歼狼,所以每個CPU在每個時刻都只有一條運行態(tài)的線程。
- 阻塞狀態(tài)(BLOCKED):java中指請求某一鎖失敗時享怀, 線程會進入阻塞態(tài)羽峰, 阻塞態(tài)會不斷地請求資源, 一旦請求成功就會進入就緒隊列, 等待CPU分配限寞。
- 等待狀態(tài)(WAITING):無線等待忍啸, 當(dāng)線程中調(diào)用 wait, join, park等函數(shù)時,線程進入等待狀態(tài)履植,等待線程會釋放CPU以及鎖資源计雌,進入等待隊列, 需要其他線程指示才能繼續(xù)運行玫霎。
- 超時等待態(tài) (TIMED_WAITING): 有限等待凿滤,與等待態(tài)的區(qū)別是, 到了超時時間會進入阻塞隊列庶近, 開始競爭鎖翁脆。
- 終止態(tài)
5.1死鎖問題
模擬死鎖問題:
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}
private static class Thread1 implements Runnable{
@Override
public void run() {
synchronized (obj1){
System.out.println("Thread1 拿到了 obj1 的鎖!");
try {
// 停頓2秒的意義在于鼻种,讓Thread2線程拿到obj2的鎖
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2){
System.out.println("Thread1 拿到了 obj2 的鎖反番!");
}
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run() {
synchronized (obj2){
System.out.println("Thread2 拿到了 obj2 的鎖!");
try {
// 停頓2秒的意義在于叉钥,讓Thread1線程拿到obj1的鎖
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1){
System.out.println("Thread2 拿到了 obj1 的鎖罢缸!");
}
}
}
}
線程1和線程2相互持續(xù)持有對方需要的鎖資源, 造成死鎖問題投队。
使用jstack查看死鎖進程:
jstack查找除了發(fā)生死鎖進程的原因枫疆, 以及對應(yīng)的堆棧信息。
6. 使用JDK子代的 Java VisualVm
綜合的結(jié)合了以上介紹jvm性能查看的jdk自帶可視化工具:
既可以查看本地java進程敷鸦, 也可以查看遠程進程
配置遠程java進程如tomcat的啟動文件catalina.sh息楔, 允許jmx遠程監(jiān)控
JAVA_OPTS="
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false"
開放9999端口, 不需要認證扒披,關(guān)閉ssl認證