RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

關(guān)于JVM的調(diào)優(yōu)知識(shí)

我快閉嘴 ? 來源:博客園 ? 作者:我是滿意吖 ? 2022-09-14 14:54 ? 次閱讀


最近很多小伙伴跟我說,自己學(xué)了不少JVM的調(diào)優(yōu)知識(shí),但是在實(shí)際工作中卻不知道何時(shí)對(duì)JVM進(jìn)行調(diào)優(yōu)。今天,我就為大家介紹幾種JVM調(diào)優(yōu)的場(chǎng)景。

在閱讀本文時(shí),假定大家已經(jīng)了解了運(yùn)行時(shí)的數(shù)據(jù)區(qū)域和常用的垃圾回收算法,也了解了Hotspot支持的垃圾回收器。

cpu占用過高

cpu占用過高要分情況討論,是不是業(yè)務(wù)上在搞活動(dòng),突然有大批的流量進(jìn)來,而且活動(dòng)結(jié)束后cpu占用率就下降了,如果是這種情況其實(shí)可以不用太關(guān)心,因?yàn)檎?qǐng)求越多,需要處理的線程數(shù)越多,這是正常的現(xiàn)象。話說回來,如果你的服務(wù)器配置本身就差,cpu也只有一個(gè)核心,這種情況,稍微多一點(diǎn)流量就真的能夠把你的cpu資源耗盡,這時(shí)應(yīng)該考慮先把配置提升吧。

第二種情況,cpu占用率長(zhǎng)期過高 ,這種情況下可能是你的程序有那種循環(huán)次數(shù)超級(jí)多的代碼,甚至是出現(xiàn)死循環(huán)了。排查步驟如下:

(1)用top命令查看cpu占用情況

cdb877c8-33f3-11ed-ba43-dac502259ad0.png

這樣就可以定位出cpu過高的進(jìn)程。在linux下,top命令獲得的進(jìn)程號(hào)和jps工具獲得的vmid是相同的:

cde7d252-33f3-11ed-ba43-dac502259ad0.png

(2)用top -Hp命令查看線程的情況

ce22f260-33f3-11ed-ba43-dac502259ad0.png

可以看到是線程id為7287這個(gè)線程一直在占用cpu

(3)把線程號(hào)轉(zhuǎn)換為16進(jìn)制

[root@localhost~]#printf"%x"7287
1c77

記下這個(gè)16進(jìn)制的數(shù)字,下面我們要用

(4)用jstack工具查看線程棧情況

[root@localhost~]#jstack7268|grep1c77-A10
"http-nio-8080-exec-2"#16daemonprio=5os_prio=0tid=0x00007fb66ce81000nid=0x1c77runnable[0x00007fb639ab9000]
java.lang.Thread.State:RUNNABLE
atcom.spareyaya.jvm.service.EndlessLoopService.service(EndlessLoopService.java:19)
atcom.spareyaya.jvm.controller.JVMController.endlessLoop(JVMController.java:30)
atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)
atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
atjava.lang.reflect.Method.invoke(Method.java:498)
atorg.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
atorg.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
atorg.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)

通過jstack工具輸出現(xiàn)在的線程棧,再通過grep命令結(jié)合上一步拿到的線程16進(jìn)制的id定位到這個(gè)線程的運(yùn)行情況,其中jstack后面的7268是第(1)步定位到的進(jìn)程號(hào),grep后面的是(2)、(3)步定位到的線程號(hào)。

從輸出結(jié)果可以看到這個(gè)線程處于運(yùn)行狀態(tài),在執(zhí)行com.spareyaya.jvm.service.EndlessLoopService.service這個(gè)方法,代碼行號(hào)是19行,這樣就可以去到代碼的19行,找到其所在的代碼塊,看看是不是處于循環(huán)中,這樣就定位到了問題。

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能。

項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro

死鎖

死鎖并沒有第一種場(chǎng)景那么明顯,web應(yīng)用肯定是多線程的程序,它服務(wù)于多個(gè)請(qǐng)求,程序發(fā)生死鎖后,死鎖的線程處于等待狀態(tài)(WAITING或TIMED_WAITING),等待狀態(tài)的線程不占用cpu,消耗的內(nèi)存也很有限,而表現(xiàn)上可能是請(qǐng)求沒法進(jìn)行,最后超時(shí)了。在死鎖情況不多的時(shí)候,這種情況不容易被發(fā)現(xiàn)。

可以使用jstack工具來查看

(1)jps查看java進(jìn)程

[root@localhost~]#jps-l
8737sun.tools.jps.Jps
8682jvm-0.0.1-SNAPSHOT.jar

(2)jstack查看死鎖問題

由于web應(yīng)用往往會(huì)有很多工作線程,特別是在高并發(fā)的情況下線程數(shù)更多,于是這個(gè)命令的輸出內(nèi)容會(huì)十分多。jstack最大的好處就是會(huì)把產(chǎn)生死鎖的信息(包含是什么線程產(chǎn)生的)輸出到最后,所以我們只需要看最后的內(nèi)容就行了

Javastackinformationforthethreadslistedabove:
===================================================
"Thread-4":
atcom.spareyaya.jvm.service.DeadLockService.service2(DeadLockService.java:35)
-waitingtolock<0x00000000f5035ae0>(ajava.lang.Object)
-locked<0x00000000f5035af0>(ajava.lang.Object)
atcom.spareyaya.jvm.controller.JVMController.lambda$deadLock$1(JVMController.java:41)
atcom.spareyaya.jvm.controller.JVMController$$Lambda$457/1776922136.run(UnknownSource)
atjava.lang.Thread.run(Thread.java:748)
"Thread-3":
atcom.spareyaya.jvm.service.DeadLockService.service1(DeadLockService.java:27)
-waitingtolock<0x00000000f5035af0>(ajava.lang.Object)
-locked<0x00000000f5035ae0>(ajava.lang.Object)
atcom.spareyaya.jvm.controller.JVMController.lambda$deadLock$0(JVMController.java:37)
atcom.spareyaya.jvm.controller.JVMController$$Lambda$456/474286897.run(UnknownSource)
atjava.lang.Thread.run(Thread.java:748)

Found1deadlock.

發(fā)現(xiàn)了一個(gè)死鎖,原因也一目了然。

基于微服務(wù)的思想,構(gòu)建在 B2C 電商場(chǎng)景下的項(xiàng)目實(shí)戰(zhàn)。核心技術(shù)棧,是 Spring Boot + Dubbo 。未來,會(huì)重構(gòu)成 Spring Cloud Alibaba 。

項(xiàng)目地址:https://github.com/YunaiV/onemall

內(nèi)存泄漏

我們都知道,java和c++的最大區(qū)別是前者會(huì)自動(dòng)收回不再使用的內(nèi)存,后者需要程序員手動(dòng)釋放。在c++中,如果我們忘記釋放內(nèi)存就會(huì)發(fā)生內(nèi)存泄漏。但是,不要以為jvm幫我們回收了內(nèi)存就不會(huì)出現(xiàn)內(nèi)存泄漏。

程序發(fā)生內(nèi)存泄漏后,進(jìn)程的可用內(nèi)存會(huì)慢慢變少,最后的結(jié)果就是拋出OOM錯(cuò)誤。發(fā)生OOM錯(cuò)誤后可能會(huì)想到是內(nèi)存不夠大,于是把-Xmx參數(shù)調(diào)大,然后重啟應(yīng)用。這么做的結(jié)果就是,過了一段時(shí)間后,OOM依然會(huì)出現(xiàn)。最后無法再調(diào)大最大堆內(nèi)存了,結(jié)果就是只能每隔一段時(shí)間重啟一下應(yīng)用。

內(nèi)存泄漏的另一個(gè)可能的表現(xiàn)是請(qǐng)求的響應(yīng)時(shí)間變長(zhǎng)了。這是因?yàn)轭l繁發(fā)生的GC會(huì)暫停其它所有線程(Stop The World)造成的。

為了模擬這個(gè)場(chǎng)景,使用了以下的程序

importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
publicclassMain{
publicstaticvoidmain(String[]args){
Mainmain=newMain();
while(true){
try{
Thread.sleep(1);
}catch(InterruptedExceptione){
e.printStackTrace();
}
main.run();
}
}

privatevoidrun(){
ExecutorServiceexecutorService=Executors.newCachedThreadPool();
for(inti=0;i10;i++){
executorService.execute(()->{
//dosomething...
});
}
}
}

運(yùn)行參數(shù)是-Xms20m -Xmx20m -XX:+PrintGC,把可用內(nèi)存調(diào)小一點(diǎn),并且在發(fā)生gc時(shí)輸出信息,運(yùn)行結(jié)果如下

...
[GC(AllocationFailure)12776K->10840K(18432K),0.0309510secs]
[GC(AllocationFailure)13400K->11520K(18432K),0.0333385secs]
[GC(AllocationFailure)14080K->12168K(18432K),0.0332409secs]
[GC(AllocationFailure)14728K->12832K(18432K),0.0370435secs]
[FullGC(Ergonomics)12832K->12363K(18432K),0.1942141secs]
[FullGC(Ergonomics)14923K->12951K(18432K),0.1607221secs]
[FullGC(Ergonomics)15511K->13542K(18432K),0.1956311secs]
...
[FullGC(Ergonomics)16382K->16381K(18432K),0.1734902secs]
[FullGC(Ergonomics)16383K->16383K(18432K),0.1922607secs]
[FullGC(Ergonomics)16383K->16383K(18432K),0.1824278secs]
[FullGC(AllocationFailure)16383K->16383K(18432K),0.1710382secs]
[FullGC(Ergonomics)16383K->16382K(18432K),0.1829138secs]
[FullGC(Ergonomics)Exceptioninthread"main"16383K->16382K(18432K),0.1406222secs]
[FullGC(AllocationFailure)16382K->16382K(18432K),0.1392928secs]
[FullGC(Ergonomics)16383K->16382K(18432K),0.1546243secs]
[FullGC(Ergonomics)16383K->16382K(18432K),0.1755271secs]
[FullGC(Ergonomics)16383K->16382K(18432K),0.1699080secs]
[FullGC(AllocationFailure)16382K->16382K(18432K),0.1697982secs]
[FullGC(Ergonomics)16383K->16382K(18432K),0.1851136secs]
[FullGC(AllocationFailure)16382K->16382K(18432K),0.1655088secs]
java.lang.OutOfMemoryError:Javaheapspace

可以看到雖然一直在gc,占用的內(nèi)存卻越來越多,說明程序有的對(duì)象無法被回收。但是上面的程序?qū)ο蠖际嵌x在方法內(nèi)的,屬于局部變量,局部變量在方法運(yùn)行結(jié)果后,所引用的對(duì)象在gc時(shí)應(yīng)該被回收啊,但是這里明顯沒有。

為了找出到底是哪些對(duì)象沒能被回收,我們加上運(yùn)行參數(shù)-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.bin,意思是發(fā)生OOM時(shí)把堆內(nèi)存信息dump出來。運(yùn)行程序直至異常,于是得到heap.dump文件,然后我們借助eclipse的MAT插件來分析,如果沒有安裝需要先安裝。

然后File->Open Heap Dump... ,然后選擇剛才dump出來的文件,選擇Leak Suspects

cedb8212-33f3-11ed-ba43-dac502259ad0.png

MAT會(huì)列出所有可能發(fā)生內(nèi)存泄漏的對(duì)象

cef87822-33f3-11ed-ba43-dac502259ad0.png

可以看到居然有21260個(gè)Thread對(duì)象,3386個(gè)ThreadPoolExecutor對(duì)象,如果你去看一下java.util.concurrent.ThreadPoolExecutor的源碼,可以發(fā)現(xiàn)線程池為了復(fù)用線程,會(huì)不斷地等待新的任務(wù),線程也不會(huì)回收,需要調(diào)用其shutdown()方法才能讓線程池執(zhí)行完任務(wù)后停止。

其實(shí)線程池定義成局部變量,好的做法是設(shè)置成單例。

上面只是其中一種處理方法

在線上的應(yīng)用,內(nèi)存往往會(huì)設(shè)置得很大,這樣發(fā)生OOM再把內(nèi)存快照dump出來的文件就會(huì)很大,可能大到在本地的電腦中已經(jīng)無法分析了(因?yàn)閮?nèi)存不足夠打開這個(gè)dump文件)。這里介紹另一種處理辦法:

(1)用jps定位到進(jìn)程號(hào)

C:UsersspareyayaIdeaProjectsmaven-project	argetclassesorgexample
et>jps-l
24836org.example.net.Main
62520org.jetbrains.jps.cmdline.Launcher
129980sun.tools.jps.Jps
136028org.jetbrains.jps.cmdline.Launcher

因?yàn)橐呀?jīng)知道了是哪個(gè)應(yīng)用發(fā)生了OOM,這樣可以直接用jps找到進(jìn)程號(hào)135988

(2)用jstat分析gc活動(dòng)情況

jstat是一個(gè)統(tǒng)計(jì)java進(jìn)程內(nèi)存使用情況和gc活動(dòng)的工具,參數(shù)可以有很多,可以通過jstat -help查看所有參數(shù)以及含義

C:UsersspareyayaIdeaProjectsmaven-project	argetclassesorgexample
et>jstat-gcutil-t-h8248361000
TimestampS0S1EOMCCSYGCYGCTFGCFGCTGCT
29.132.810.0023.4885.9292.8484.13140.33900.0000.339
30.132.810.0078.1285.9292.8484.13140.33900.0000.339
31.10.000.0022.7091.7492.7283.71150.38910.2330.622

上面是命令意思是輸出gc的情況,輸出時(shí)間,每8行輸出一個(gè)行頭信息,統(tǒng)計(jì)的進(jìn)程號(hào)是24836,每1000毫秒輸出一次信息。

輸出信息是Timestamp是距離jvm啟動(dòng)的時(shí)間,S0、S1、E是新生代的兩個(gè)Survivor和Eden,O是老年代區(qū),M是Metaspace,CCS使用壓縮比例,YGC和YGCT分別是新生代gc的次數(shù)和時(shí)間,F(xiàn)GC和FGCT分別是老年代gc的次數(shù)和時(shí)間,GCT是gc的總時(shí)間。雖然發(fā)生了gc,但是老年代內(nèi)存占用率根本沒下降,說明有的對(duì)象沒法被回收(當(dāng)然也不排除這些對(duì)象真的是有用)。

(3)用jmap工具dump出內(nèi)存快照

jmap可以把指定java進(jìn)程的內(nèi)存快照dump出來,效果和第一種處理辦法一樣,不同的是它不用等OOM就可以做到,而且dump出來的快照也會(huì)小很多。

jmap-dump:live,format=b,file=heap.bin24836

這時(shí)會(huì)得到heap.bin的內(nèi)存快照文件,然后就可以用eclipse來分析了。

總結(jié)

以上三種嚴(yán)格地說還算不上jvm的調(diào)優(yōu),只是用了jvm工具把代碼中存在的問題找了出來。我們進(jìn)行jvm的主要目的是盡量減少停頓時(shí)間,提高系統(tǒng)的吞吐量。

但是如果我們沒有對(duì)系統(tǒng)進(jìn)行分析就盲目去設(shè)置其中的參數(shù),可能會(huì)得到更壞的結(jié)果,jvm發(fā)展到今天,各種默認(rèn)的參數(shù)可能是實(shí)驗(yàn)室的人經(jīng)過多次的測(cè)試來做平衡的,適用大多數(shù)的應(yīng)用場(chǎng)景。

如果你認(rèn)為你的jvm確實(shí)有調(diào)優(yōu)的必要,也務(wù)必要取樣分析,最后還得慢慢多次調(diào)節(jié),才有可能得到更優(yōu)的效果。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10854

    瀏覽量

    211570
  • JVM
    JVM
    +關(guān)注

    關(guān)注

    0

    文章

    158

    瀏覽量

    12220

原文標(biāo)題:幾種常見的JVM調(diào)優(yōu)場(chǎng)景(建議收藏)

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    java開發(fā)人員不了解jvm調(diào)優(yōu)對(duì)工作有影響嗎

    作為一名java開發(fā)人員,不了解jvm調(diào)優(yōu)對(duì)工作有什么影響?
    發(fā)表于 04-10 11:57

    JVM性能指標(biāo)分析

    JVM性能調(diào)優(yōu)實(shí)踐——JVM
    發(fā)表于 10-17 15:00

    如何對(duì)電機(jī)進(jìn)行調(diào)優(yōu)?調(diào)優(yōu)的好處是什么?

    如何自動(dòng)對(duì)電機(jī)進(jìn)行調(diào)優(yōu)
    的頭像 發(fā)表于 08-22 00:03 ?3127次閱讀

    Linux用電功耗調(diào)優(yōu)的筆記分享

    整理一些Linux用電功耗調(diào)優(yōu)的筆記,分享給小伙伴,關(guān)于用電調(diào)優(yōu)個(gè)人覺得
    的頭像 發(fā)表于 06-23 15:19 ?4091次閱讀

    Alluxio線程池結(jié)構(gòu)與吞吐量調(diào)優(yōu)

    本文介紹了 Alluxio Master 的線程池結(jié)構(gòu)與每個(gè)線程的功能。在調(diào)優(yōu)過程中,利用分析結(jié)果調(diào)整審計(jì)日志的 blocking queue,調(diào)整 UFS-SYNC-PREFETCH 線程數(shù),調(diào)
    發(fā)表于 11-11 11:36 ?598次閱讀

    基于專家知識(shí)+AI算法的性能調(diào)優(yōu)

    操作系統(tǒng)的軟硬件配置過于繁雜。無論是應(yīng)用內(nèi)核參數(shù)、系統(tǒng)服務(wù)還是應(yīng)用配置,都有成百上千的參數(shù),而且參數(shù)之間相互,在調(diào)優(yōu)的時(shí)候往往存在蹺蹺板現(xiàn)象,如何管理如此大量的領(lǐng)域知識(shí),是非常困難的事情。
    的頭像 發(fā)表于 03-07 10:54 ?1004次閱讀

    JVM知識(shí)體系剖析

    從源碼到運(yùn)行、類加載,再到內(nèi)存分配和垃圾回收,以及JVM調(diào)優(yōu)的技巧與實(shí)戰(zhàn)。 理論-實(shí)戰(zhàn)-面試三結(jié)合,帶大家剖析整個(gè)JVM知識(shí)體系,一站解決
    的頭像 發(fā)表于 10-10 11:37 ?426次閱讀
    <b class='flag-5'>JVM</b><b class='flag-5'>知識(shí)</b>體系剖析

    javajvm調(diào)優(yōu)有幾種方法

    JVM調(diào)優(yōu)是Java應(yīng)用程序性能優(yōu)化過程中的重要步驟,它通過針對(duì)JVM進(jìn)行優(yōu)化來提高應(yīng)用程序的性能和可靠性。JVM
    的頭像 發(fā)表于 12-05 11:11 ?2109次閱讀

    什么場(chǎng)景需要jvm調(diào)優(yōu)

    JVM調(diào)優(yōu)是指對(duì)Java虛擬機(jī)進(jìn)行性能優(yōu)化和資源管理,以提高應(yīng)用程序的運(yùn)行效率和吞吐量。JVM調(diào)優(yōu)
    的頭像 發(fā)表于 12-05 11:14 ?1427次閱讀

    jvm調(diào)優(yōu)參數(shù)

    JVM(Java虛擬機(jī))是Java程序的運(yùn)行環(huán)境,它負(fù)責(zé)解釋Java字節(jié)碼并執(zhí)行相應(yīng)的指令。為了提高應(yīng)用程序的性能和穩(wěn)定性,我們可以調(diào)優(yōu)JVM的參數(shù)。
    的頭像 發(fā)表于 12-05 11:29 ?623次閱讀

    jvm參數(shù)的設(shè)置和jvm調(diào)優(yōu)

    JVM(Java虛擬機(jī))參數(shù)的設(shè)置和調(diào)優(yōu)對(duì)于提高Java應(yīng)用程序的性能和穩(wěn)定性非常重要。在本文中,我們將詳細(xì)介紹JVM參數(shù)的設(shè)置和調(diào)
    的頭像 發(fā)表于 12-05 11:36 ?1555次閱讀

    jvm調(diào)優(yōu)主要是調(diào)哪里

    JVM調(diào)優(yōu)主要涉及內(nèi)存管理、垃圾回收、線程管理與鎖優(yōu)化等方面。下面將詳細(xì)介紹每個(gè)方面的調(diào)優(yōu)技術(shù)和策略以及如何進(jìn)行優(yōu)化。 內(nèi)存管理
    的頭像 發(fā)表于 12-05 11:37 ?1554次閱讀

    jvm調(diào)優(yōu)常用命令

    JVM調(diào)優(yōu)是提升Java應(yīng)用性能的一個(gè)重要方面,通過合理設(shè)置JVM參數(shù)可以達(dá)到優(yōu)化應(yīng)用性能、提高系統(tǒng)穩(wěn)定性的目的。本文將為你詳細(xì)介紹JVM
    的頭像 發(fā)表于 12-05 11:43 ?694次閱讀

    jvm調(diào)優(yōu)工具有哪些

    JVM調(diào)優(yōu)是提高Java應(yīng)用程序性能的重要手段,而JVM調(diào)優(yōu)工具則是輔助開發(fā)人員進(jìn)行
    的頭像 發(fā)表于 12-05 11:44 ?1067次閱讀

    深度解析JVM調(diào)優(yōu)實(shí)踐應(yīng)用

    Tomcat自身的調(diào)優(yōu)是針對(duì)conf/server.xml中的幾個(gè)參數(shù)的調(diào)優(yōu)設(shè)置。首先是對(duì)這幾個(gè)參數(shù)的含義要有深刻而清楚的理解。
    的頭像 發(fā)表于 04-01 10:24 ?457次閱讀
    深度解析<b class='flag-5'>JVM</b><b class='flag-5'>調(diào)</b><b class='flag-5'>優(yōu)</b>實(shí)踐應(yīng)用
    RM新时代网站-首页