一挥吵、Jps
查看當(dāng)前正在運(yùn)行的java程序篷店,比如我們?cè)谶@里啟動(dòng)自己的項(xiàng)目以后使用該命令
二祭椰、Jinfo
查看正在運(yùn)行的java應(yīng)用程序的擴(kuò)展參數(shù)。從上一步獲取到了我們自己的程序的進(jìn)程id為9749疲陕,然后使用jinfo查看該程序的擴(kuò)展參數(shù)
2.1 查看jvm參數(shù)
jinfo -flags 9749
2.2 查看系統(tǒng)參數(shù)
jinfo -sysprops 9749
三方淤、Jstat
jstat命令可以查看堆內(nèi)存中各部分的使用量,以及加載類(lèi)的數(shù)量
3.1 類(lèi)加載統(tǒng)計(jì)
jstat -class 9749
可以看到加載了13619個(gè)類(lèi)蹄殃,這些類(lèi)的總大小為25286.0字節(jié)
- Loaded:加載class的數(shù)量
- Bytes:所占用空間大小
- Unloaded:未加載數(shù)量
- Bytes:未加載占用空間
- Time:時(shí)間
3.2 垃圾回收統(tǒng)計(jì)
jstat -gc 9749
S0C:survivor區(qū)中的from區(qū)配置大小携茂,jvm中為s0區(qū),capacity 容量 S0C=S0 capacity
S1C:survivor區(qū)中的to區(qū)配置大小诅岩,jvm中為s1區(qū)
S0U:survivor區(qū)中的from區(qū)使用大小
S1U:survivor區(qū)中的to區(qū)使用大小
EC:伊甸園eden區(qū)的大小
EU:伊甸園區(qū)的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法區(qū)大小(元空間)
MU:方法區(qū)使用大小
CCSC:壓縮類(lèi)空間大小 Compressed class space capacity https://www.zhihu.com/question/268392125
CCSU:壓縮類(lèi)空間使用大小
YGC:年輕代垃圾回收次數(shù)
YGCT:年輕代垃圾回收消耗時(shí)間讳苦,已收集次數(shù)累計(jì)時(shí)間
FGC:老年代垃圾回收次數(shù)
FGCT:老年代垃圾回收消耗時(shí)間
GCT:垃圾回收消耗總時(shí)間 75.807+0.991=76.798
3.3 堆內(nèi)存統(tǒng)計(jì)
和上面的結(jié)果會(huì)有一些重復(fù)的參數(shù),但是同樣有他自己的參數(shù)
jstat -gccapacity 4420
NGCMN:新生代最小容量 new generation capacity min
NGCMX:新生代最大容量 new generation capacity max
NGC:當(dāng)前新生代容量
S0C:survivor中from區(qū)容量
S1C:survivor中to區(qū)容量
EC:eden區(qū)容量
OGCMN:老年代最小容量 old generation capacity min
OGCMX:老年代最大容量
OGC:當(dāng)前老年代大小
OC:當(dāng)前老年代大小
MCMN:最小元數(shù)據(jù)容量 metaspace capacity min 最小元空間容量
MCMX:最大元數(shù)據(jù)容量
MC:當(dāng)前元數(shù)據(jù)空間大小
CCSMN:最小壓縮類(lèi)空間大小
CCSMX:最大壓縮類(lèi)空間大小
CCSC:當(dāng)前壓縮類(lèi)空間大小
YGC:年輕代gc次數(shù)
FGC:老年代GC次數(shù)
3.4 新生代垃圾回收統(tǒng)計(jì)
jstat -gcnew 9749
- S0C:第一個(gè)幸存區(qū)的大小
- S1C:第二個(gè)幸存區(qū)的大小
- S0U:第一個(gè)幸存區(qū)的使用大小
- S1U:第二個(gè)幸存區(qū)的使用大小
- TT:對(duì)象在新生代存活的次數(shù)
- MTT : 對(duì)象在新生代存活的最大次數(shù)
- DSS : 期望的幸存區(qū)大小
- EC:伊甸園區(qū)的大小
- EU:伊甸園區(qū)的使用大小
- YGC:年輕代垃圾回收次數(shù)
- YGCT:年輕代垃圾回收消耗時(shí)間
3.4 新生代內(nèi)存統(tǒng)計(jì)
jstat -gcnewcapacity 9749
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:當(dāng)前新生代容量
S0CMX:最大幸存1區(qū)大小
S0C:當(dāng)前幸存1區(qū)大小
S1CMX:最大幸存2區(qū)大小
S1C:當(dāng)前幸存2區(qū)大小
ECMX:最大伊甸園區(qū)大小
EC:當(dāng)前伊甸園區(qū)大小
YGC:年輕代垃圾回收次數(shù)
FGC:老年代回收次數(shù)
3.5 老年代垃圾回收統(tǒng)計(jì)
jstat -gcold 9749
MC:方法區(qū)大小
MU:方法區(qū)使用大小
CCSC:壓縮類(lèi)空間大小
CCSU:壓縮類(lèi)空間使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年輕代垃圾回收次數(shù)
FGC:老年代垃圾回收次數(shù)
FGCT:老年代垃圾回收消耗時(shí)間
GCT:垃圾回收消耗總時(shí)間
3.6 老年代內(nèi)存統(tǒng)計(jì)
jstat -gcoldcapacity 9749
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:當(dāng)前老年代大小
OC:老年代大小
YGC:年輕代垃圾回收次數(shù)
FGC:老年代垃圾回收次數(shù)
FGCT:老年代垃圾回收消耗時(shí)間
GCT:垃圾回收消耗總時(shí)間
3.7 元數(shù)據(jù)空間統(tǒng)計(jì)
jstat -gcmetacapacity 9749
MCMN:最小元數(shù)據(jù)容量 meta capacity min
MCMX:最大元數(shù)據(jù)容量
MC:當(dāng)前元數(shù)據(jù)空間大小
CCSMN:最小壓縮類(lèi)空間大小
CCSMX:最大壓縮類(lèi)空間大小
CCSC:當(dāng)前壓縮類(lèi)空間大小
YGC:年輕代垃圾回收次數(shù)
FGC:老年代垃圾回收次數(shù)
FGCT:老年代垃圾回收消耗時(shí)間
GCT:垃圾回收消耗總時(shí)間
3.8 總結(jié)垃圾回收統(tǒng)計(jì)
jstat -gcutil 4420
S0:幸存1區(qū)當(dāng)前使用比例
S1:幸存2區(qū)當(dāng)前使用比例
E:伊甸園區(qū)使用比例
O:老年代使用比例
M:元數(shù)據(jù)區(qū)使用比例
CCS:壓縮類(lèi)使用比例
YGC:年輕代垃圾回收次數(shù)
FGC:老年代垃圾回收次數(shù)
FGCT:老年代垃圾回收消耗時(shí)間
GCT:垃圾回收消耗總時(shí)間
四吩谦、Jmap
此命令用來(lái)查看內(nèi)存信息
4.1 實(shí)例個(gè)數(shù)以及內(nèi)存占用大性铡(會(huì)導(dǎo)致停頓,生產(chǎn)環(huán)境慎用)
jmap -histo 4420 > ./log.txt
- num:序號(hào)
- instances:實(shí)例數(shù)量
- bytes:占用空間大小
- class name:類(lèi)名稱
4.2 堆信息
jmap -heap 9749
4.3 堆內(nèi)存dump文件
4.3.1 手動(dòng)導(dǎo)出堆內(nèi)存dump文件
可以導(dǎo)出導(dǎo)出那一刻的程序堆運(yùn)行內(nèi)存dump文件逮京,描述了那一刻的程序堆內(nèi)存詳情(和那一刻線程的快照)卿堂,我們一般使用dump主要是查內(nèi)存的使用情況
當(dāng)然也可以用jvisualvm導(dǎo)出堆dump/線程dump
jmap -dump:format=b,file=radient.hprof 2964
該dump文件我們可以直接加載到j(luò)visualvm中進(jìn)行查看
在類(lèi)選項(xiàng)中點(diǎn)進(jìn)去任何一個(gè)類(lèi)束莫,還可以查看每個(gè)類(lèi)實(shí)例的詳細(xì)信息懒棉,比如值
4.3.2 自動(dòng)導(dǎo)出堆內(nèi)存dump文件
我們可以設(shè)置當(dāng)oom內(nèi)存溢出時(shí)候自動(dòng)導(dǎo)出dump文件(內(nèi)存占用比較大的時(shí)候,可能會(huì)導(dǎo)不出來(lái))览绿,下面我們使用一個(gè)能導(dǎo)致oom的案例程序演示如何自動(dòng)導(dǎo)出dump
// 因?yàn)樾陆ǔ鰜?lái)的對(duì)象一直在list中策严,一直在被引用沒(méi)有被回收,因此堆內(nèi)存遲早有滿了的一天
public class Test {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
int i =0;
for(;;){
list.add(new R(i++,UUID.randomUUID().toString(),null));
}
}
}
配置jvm啟動(dòng)參數(shù)(為了更快oom饿敲,設(shè)置最小最大運(yùn)行堆內(nèi)存10M妻导,輸出GC日志,oom時(shí)候自動(dòng)導(dǎo)出dump文件)
-Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\develop\jvm.dump
上述程序的運(yùn)行結(jié)果如下怀各,其中黃色字體下面的就是GCdetail參數(shù)打印出來(lái)的各個(gè)內(nèi)存空間使用情況
另外倔韭,雖然我們自動(dòng)導(dǎo)出的dump文件后綴名和之前的手動(dòng)導(dǎo)出的dump文件后綴名不一樣,但是其實(shí)是一個(gè)東西都是我們程序運(yùn)行的那一刻的內(nèi)存分配的快照瓢对,然后我們將上述dump文件導(dǎo)入到j(luò)visualvm中進(jìn)行分析
有內(nèi)存溢出情況寿酌,實(shí)質(zhì)上是有一些異常的類(lèi)
我們可以看到R類(lèi)和integer,string類(lèi) 硕蛹,都是new R的時(shí)候產(chǎn)生的醇疼,占據(jù)內(nèi)存最大的是char字符數(shù)組硕并,5m,接近一半的空間秧荆,這是為什么呢倔毙?
從string類(lèi)點(diǎn)進(jìn)去,查看value會(huì)繼續(xù)進(jìn)入到char數(shù)組乙濒,可以查看到每個(gè)R的msg內(nèi)容即UUID陕赃,我們知道new 一個(gè)string,實(shí)際是用char數(shù)組存儲(chǔ)的在string類(lèi)內(nèi)部
五琉兜、Jstack
我們下面將采用jstack來(lái)分析死鎖程序凯正,下面是死鎖示例:
public class Test2 {
private static Object o1= new Object();
private static Object o2= new Object();
public static void main(String[] args) {
Thread t1= new Thread(() -> {
synchronized (o1) {
try {
System.out.println("線程1開(kāi)始");
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("線程1結(jié)束");
}
}
});
Thread t2= new Thread(() -> {
synchronized (o2) {
try {
System.out.println("線程2開(kāi)始");
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
System.out.println("線程2結(jié)束");
}
}
});
t1.start();
t2.start();
System.out.println("主線程提前結(jié)束,但是進(jìn)程并未退出(由于t1和t2線程導(dǎo)致進(jìn)程中出現(xiàn)死鎖)");
}
}
用jstack查看死鎖的命令
jstack 16900 > deadlock.txt
可以看到上面是一些線程信息豌蟋,在最底下輸出了發(fā)現(xiàn)java層次的死鎖的信息
從圖上我們可以一目了然的發(fā)現(xiàn)兩個(gè)線程處于等待對(duì)方持有的鎖的阻塞狀態(tài)
下面我們使用jvisualvm來(lái)查看死鎖廊散,可以看到自動(dòng)為我們分析出了死鎖
jstack找出占用cpu最高的堆棧信息
1,使用命令top -p <pid> 梧疲,顯示你的java進(jìn)程的內(nèi)存情況允睹,pid是你的java進(jìn)程號(hào),比如9749
2幌氮,按H缭受,獲取每個(gè)線程的內(nèi)存情況
3,找到內(nèi)存和cpu占用最高的線程tid该互,比如4977
4米者,轉(zhuǎn)為十六進(jìn)制得到 0x1371 ,此為線程id的十六進(jìn)制表示
5,執(zhí)行 jstack 9749|grep 1371 -A 120 宇智,得到線程堆棧信息中1371這個(gè)線程所在行的后面10行
6蔓搞,查看對(duì)應(yīng)的堆棧信息找出可能存在問(wèn)題的代碼
5.2 Jstack Dump 日志文件中的線程狀態(tài)
dump 文件里,值得關(guān)注的線程狀態(tài)有:
-
死鎖随橘,Deadlock(重點(diǎn)關(guān)注)
死鎖線程,一般指多個(gè)線程調(diào)用間机蔗,進(jìn)入相互資源占用蒲祈,導(dǎo)致一直等待無(wú)法釋放的情況 - 執(zhí)行中,Runnable
一般指該線程正在執(zhí)行狀態(tài)中萝嘁,該線程占用了資源梆掸,正在處理某個(gè)請(qǐng)求,有可能正在傳遞SQL到數(shù)據(jù)庫(kù)執(zhí)行牙言,有可能在對(duì)某個(gè)文件操作酸钦,有可能進(jìn)行數(shù)據(jù)類(lèi)型等轉(zhuǎn)換 -
等待資源,Waiting on condition(重點(diǎn)關(guān)注)
等待資源嬉挡,或等待某個(gè)條件的發(fā)生钝鸽。具體原因需結(jié)合 stacktrace來(lái)分析- 如果堆棧信息明確是應(yīng)用代碼汇恤,則證明該線程正在等待資源。一般是大量讀取某資源拔恰,且該資源采用了資源鎖的情況下因谎,線程進(jìn)入等待狀態(tài),等待資源的讀取颜懊。
- 又或者财岔,正在等待其他線程的執(zhí)行等。
- 如果發(fā)現(xiàn)有大量的線程都在處在 Wait on condition河爹,從線程 stack看匠璧,正等待網(wǎng)絡(luò)讀寫(xiě),這可能是一個(gè)網(wǎng)絡(luò)瓶頸的征兆咸这。因?yàn)榫W(wǎng)絡(luò)阻塞導(dǎo)致線程無(wú)法執(zhí)行夷恍。
3.1 一種情況是網(wǎng)絡(luò)非常忙,幾乎消耗了所有的帶寬媳维,仍然有大量數(shù)據(jù)等待網(wǎng)絡(luò)讀寫(xiě)酿雪;
3.2 另一種情況也可能是網(wǎng)絡(luò)空閑,但由于路由等問(wèn)題侄刽,導(dǎo)致包無(wú)法正常的到達(dá)指黎。 - 另外一種出現(xiàn) Wait on condition的常見(jiàn)情況是該線程在 sleep,等待 sleep的時(shí)間到了時(shí)候州丹,將被喚醒醋安。
- 等待獲取監(jiān)視器,Waiting on monitor entry(重點(diǎn)關(guān)注)
- 暫停墓毒,Suspended
- 對(duì)象等待中吓揪,Object.wait() 或 TIMED_WAITING
- 阻塞,Blocked(重點(diǎn)關(guān)注)
- 停止蚁鳖,Parked
Waiting for monitor entry 和 in Object.wait():Monitor是 Java中用以實(shí)現(xiàn)線程之間的互斥與協(xié)作的主要手段磺芭,它可以看成是對(duì)象或者 Class的鎖赁炎。每一個(gè)對(duì)象都有醉箕,也僅有一個(gè) monitor。從下圖1中可以看出徙垫,每個(gè) Monitor在某個(gè)時(shí)刻讥裤,只能被一個(gè)線程擁有,該線程就是 “Active Thread”姻报,而其它線程都是 “Waiting Thread”己英,分別在兩個(gè)隊(duì)列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的線程狀態(tài)是 “Waiting for monitor entry”吴旋,而在 “Wait Set”中等待的線程狀態(tài)是 “in Object.wait()”
"http-nio-7901-exec-11" 200 daemon prio=5 os_prio=0 tid=0x00007fe45c001800 nid=0x473e waiting on condition [0x00007fe48cbed000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000070e3924c8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:89)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:33)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
六损肛、遠(yuǎn)程連接jvisualvm
http://blog.sina.com.cn/s/blog_92e2ec4f0100vz35.html
https://blog.csdn.net/qq_39575279/article/details/105403597
注意:Djava.rmi.server.hostname為本機(jī)的ip地址厢破,如果為云服務(wù)器,則為云服務(wù)器的公網(wǎng)ip
七治拿、-Xms摩泪、-Xmn參數(shù)的含義:
答:
堆內(nèi)存分配:
JVM初始分配的內(nèi)存由-Xms指定,默認(rèn)是物理內(nèi)存的1/64
JVM最大分配的內(nèi)存由-Xmx指定劫谅,默認(rèn)是物理內(nèi)存的1/4
默認(rèn)空余堆內(nèi)存小于40%時(shí)见坑,JVM就會(huì)增大堆直到-Xmx的最大限制;空余堆內(nèi)存大于70%時(shí)捏检,JVM會(huì)減少堆直到 -Xms的最小限制荞驴。
因此服務(wù)器一般設(shè)置-Xms、-Xmx相等以避免在每次GC 后調(diào)整堆的大小贯城。對(duì)象的堆內(nèi)存由稱為垃圾回收器的自動(dòng)內(nèi)存管理系統(tǒng)回收熊楼。
非堆內(nèi)存分配:
JVM使用-XX:PermSize設(shè)置非堆內(nèi)存初始值,默認(rèn)是物理內(nèi)存的1/64能犯;
由XX:MaxPermSize設(shè)置最大非堆內(nèi)存的大小孙蒙,默認(rèn)是物理內(nèi)存的1/4。
-Xmn2G:設(shè)置年輕代大小為2G悲雳。
-XX:SurvivorRatio挎峦,設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的比值