? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 應(yīng)用程序內(nèi)存泄漏問(wèn)題排查? ? ? ??
創(chuàng)新互聯(lián)是一家專(zhuān)注于成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站與策劃設(shè)計(jì),漯河網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)10多年,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:漯河等地區(qū)。漯河做網(wǎng)站價(jià)格咨詢:189820811081.文章的由來(lái);
在日常運(yùn)維過(guò)程中,會(huì)遇到服務(wù)器資源居高不下,或者CPU內(nèi)存暴漲問(wèn)題而引發(fā)的oom導(dǎo)致服務(wù)不可用 (大多數(shù)程序都是java應(yīng)用),由此編寫(xiě)了該文章,為工作排查問(wèn)題參考依據(jù)和快速定位問(wèn)題方法;
2.基礎(chǔ)知識(shí)儲(chǔ)備;
?(1).jvm 配置常見(jiàn)參數(shù):
堆參數(shù)參數(shù)
參數(shù) | ?描述 |
-Xms? | ?設(shè)置?JVM啟動(dòng)時(shí)堆內(nèi)存的初始化大小 |
-Xmx | ?設(shè)置堆內(nèi)存大值 |
-Xmn | ?設(shè)置年輕代的空間大小,剩下的為年老代的空間大小 |
-XX:PermGen | ?設(shè)置永久代的內(nèi)存初始化大小(JDK1.8 開(kāi)始廢棄永久代) |
-XX:MaxPermGen | ?設(shè)置永久代的大值 |
-XX:SurvivorRatio?? | ?設(shè)置Eden區(qū)和Survivor區(qū)的空間比例:Eden/S0 =Eden/S1 默認(rèn)8 |
-XX:NewRatio | ?設(shè)置年老代和年輕代的比例大小,默認(rèn)值是2 |
-XX:+UseSerialGC ? ? | 串行,young (年輕區(qū))和Old(老年區(qū))都使用串行,使用復(fù)制算法回收,邏輯簡(jiǎn)單高效,無(wú)現(xiàn)場(chǎng)切換開(kāi)銷(xiāo) |
-XX:+UseParallelGC | 并行, young (年輕區(qū))使用?Parallel scavenge 回收算法,會(huì)產(chǎn)生多個(gè)線程并行回收。通過(guò)-XX:ParallelGCThreads=n 參數(shù)指定有線程數(shù),默認(rèn)為cpu核數(shù),Old(老年區(qū)):單線程 |
-XX:+UseParallelOldGC | 并行 和UseParallelGC 一樣,young (年輕區(qū)) 和Old(老年區(qū)) 垃圾回收都使用多線程收集 |
-XX:+UseConMarkSweepGC | 并發(fā),短暫停頓的并發(fā)收集,young區(qū)可以使用普通的或者parallel垃圾回收算法,由參數(shù) --XX:+UseParNewGC 來(lái)控制; Old區(qū) 只使用Concurrent Mark Sweep |
-XX:+UseG1GC | 并行的,并發(fā)的和增量式短暫停頓的垃圾收集器。不區(qū)分young (年輕區(qū)) 和Old(老年區(qū))空間。它把堆空間分為多個(gè)大小相等的區(qū)域。當(dāng)進(jìn)行垃圾收集時(shí),他會(huì)優(yōu)先收集存活對(duì)象較少的區(qū)域。因此叫"Garbage First” |
如上表所示,目前主要有串行、并行和并發(fā)三種,對(duì)于大內(nèi)存的應(yīng)用而言,串行的性能太低,因此使用到的主要是并行和并發(fā)兩種。并行和并發(fā) GC 的策略通過(guò) UseParallelGC 和 UseConcMarkSweepGC 來(lái)指定,還有一些細(xì)節(jié)的配置參數(shù)用來(lái)配置策略的執(zhí)行方式。例如:XX:ParallelGCThreads, XX:CMSInitiatingOccupancyFraction 等。 通常:Young 區(qū)對(duì)象回收只可選擇并行(耗時(shí)間),Old 區(qū)選擇并發(fā)(耗 CPU)。
生產(chǎn)環(huán)境服務(wù)配置參數(shù)
? ? 系統(tǒng)版本 | cpu | mem | jdk 版本 | jvm配置 | 備注 |
CentOS 7.5.1804 (Core)? | 8C | 8G | java-1.8.0_121 | java? -Xmx5g -Xms5g -Xmn3g -XX:MetaspaceSize=32m -XX:MaxMetaspaceSize=128m -XX:MaxDirectMemorySize=512m -XX:+UseCompressedClassPointers -XX:CompressedClassSpaceSize=64m -XX:+UseConcMarkSweepGC -XX:ParallelCMSThreads=2 -XX:+CMSClassUnloadingEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=80 -verbose:gc -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+PrintCommandLineFlags -XX:+ExplicitGCInvokesConcurrent -Xloggc:/data/log/app-web/gc.log -Dapp.id=app-web -Dapollo.bootstrap.enabled=true -Duser.timezone=GMT+8 -Dapollo.bootstrap.namespaces=application -Deureka.instance.metadata-map.zone=zone-1 -Dservice.name=app-web -Denv=prod -Dprod_meta=http://configserver-prod.com -Druntime.env=prod -jar ./lib/app-web-1.8.10.0.jar | 微服務(wù) springcloud 框架 |
常見(jiàn)組合
GC 調(diào)優(yōu)原則
在調(diào)優(yōu)之前,我們需要記住下面的原則:
多數(shù)的 Java 應(yīng)用不需要在服務(wù)器上進(jìn)行 GC 優(yōu)化;
多數(shù)導(dǎo)致 GC 問(wèn)題的 Java 應(yīng)用,都不是因?yàn)槲覀儏?shù)設(shè)置錯(cuò)誤,而是代碼問(wèn)題;
在應(yīng)用上線之前,先考慮將機(jī)器的 JVM 參數(shù)設(shè)置到最優(yōu)(最適合);
減少創(chuàng)建對(duì)象的數(shù)量;
減少使用全局變量和大對(duì)象;
GC 優(yōu)化是到最后不得已才采用的手段;
在實(shí)際使用中,分析 GC 情況優(yōu)化代碼比優(yōu)化 GC 參數(shù)要多得多。
將轉(zhuǎn)移到老年代的對(duì)象數(shù)量降低到最?。?/p>
減少 GC 的執(zhí)行時(shí)間。
策略 1:將新對(duì)象預(yù)留在新生代,由于 Full GC 的成本遠(yuǎn)高于 Minor GC,因此盡可能將對(duì)象分配在新生代是明智的做法,實(shí)際項(xiàng)目中根據(jù) GC 日志分析新生代空間大小分配是否合理,適當(dāng)通過(guò)“-Xmn”命令調(diào)節(jié)新生代大小,大限度降低新對(duì)象直接進(jìn)入老年代的情況。
策略 2:大對(duì)象進(jìn)入老年代,雖然大部分情況下,將對(duì)象分配在新生代是合理的。但是對(duì)于大對(duì)象這種做法卻值得商榷,大對(duì)象如果首次在新生代分配可能會(huì)出現(xiàn)空間不足導(dǎo)致很多年齡不夠的小對(duì)象被分配的老年代,破壞新生代的對(duì)象結(jié)構(gòu),可能會(huì)出現(xiàn)頻繁的 full gc。因此,對(duì)于大對(duì)象,可以設(shè)置直接進(jìn)入老年代(當(dāng)然短命的大對(duì)象對(duì)于垃圾回收老說(shuō)簡(jiǎn)直就是噩夢(mèng))。-XX:PretenureSizeThreshold 可以設(shè)置直接進(jìn)入老年代的對(duì)象大小。
策略 3:合理設(shè)置進(jìn)入老年代對(duì)象的年齡,-XX:MaxTenuringThreshold 設(shè)置對(duì)象進(jìn)入老年代的年齡大小,減少老年代的內(nèi)存占用,降低 full gc 發(fā)生的頻率。
策略 4:設(shè)置穩(wěn)定的堆大小,堆大小設(shè)置有兩個(gè)參數(shù):-Xms 初始化堆大小,-Xmx 大堆大小。
策略5:注意: 如果滿足下面的指標(biāo),則一般不需要進(jìn)行 GC 優(yōu)化:
MinorGC 執(zhí)行時(shí)間不到50ms;
Minor GC 執(zhí)行不頻繁,約10秒一次;
Full GC 執(zhí)行時(shí)間不到1s;
Full GC 執(zhí)行頻率不算頻繁,不低于10分鐘1次。
3.排查技巧戰(zhàn)
1.內(nèi)存泄漏排查技巧
系統(tǒng)命令
1.登陸探測(cè)服務(wù)器,首先是 top free df 三連;
jstat 是一個(gè)非常強(qiáng)大的 JVM 監(jiān)控工具,一般用法是:jstat [-options] pid interval
它支持的查看項(xiàng)有:
-class 查看類(lèi)加載信息
-compile 編譯統(tǒng)計(jì)信息
-gc 垃圾回收信息
-gcXXX 各區(qū)域 GC 的詳細(xì)信息 如 -gcold
3.?jstat -gc pid [interval] 查看??java 進(jìn)程的 GC 狀態(tài);
關(guān)注指標(biāo):?FULL GC 。
4.?jstack pid > jstack.log 查詢線程棧 并保存現(xiàn)場(chǎng);
棧的分析很簡(jiǎn)單,看一下線程數(shù)是不是過(guò)多,多數(shù)棧都在干嘛。
> grep 'java.lang.Thread.State' jstack.log ?| wc -l
> 464
過(guò)濾進(jìn)程
grep -A 1 'java.lang.Thread.State' jstack.log ?| grep -v 'java.lang.Thread.State' | sort | uniq -c |sort -n
? ? 10 ? ? at java.lang.Class.forName0(Native Method)
? ? 10 ? ? at java.lang.Object.wait(Native Method)
? ? 16 ? ? at java.lang.ClassLoader.loadClass(ClassLoader.java:404)
? ? 44 ? ? at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
? ?344 ? ? at sun.misc.Unsafe.park(Native Method)
線程狀態(tài)好像也無(wú)異常,接下來(lái)分析堆文件。
內(nèi)存堆dump??
5.使用 jmap -dump:format=b,file=heap.log pid 保存了堆現(xiàn)場(chǎng),然后重啟了應(yīng)用服務(wù)
堆文件都是一些二進(jìn)制數(shù)據(jù),在命令行查看非常麻煩,Java 為我們提供的工具都是可視化的,Linux 服務(wù)器上又沒(méi)法查看,那么首先要把文件下載到本地。
由于我們?cè)O(shè)置的堆內(nèi)存為 4G,所以 dump 出來(lái)的堆文件也很大,下載它確實(shí)非常費(fèi)事,不過(guò)我們可以先對(duì)它進(jìn)行一次壓縮。=
gzip 是個(gè)功能很強(qiáng)大的壓縮命令,特別是我們可以設(shè)置 -1 ~ -9 來(lái)指定它的壓縮級(jí)別,數(shù)據(jù)越大壓縮比率越大,耗時(shí)也就越長(zhǎng),推薦使用 -6~7, -9 實(shí)在是太慢了,且收益不大,有這個(gè)壓縮的時(shí)間,多出來(lái)的文件也下載好了。
MAT 是分析 Java 堆內(nèi)存的利器,使用它打開(kāi)我們的堆文件(將文件后綴改為 .hprof), 它會(huì)提示我們要分析的種類(lèi),果斷選擇 memory leak suspect。
windows 系統(tǒng)安裝包如下:
https://www.eclipse.org/downloads/download.php?file=/mat/1.9.1/rcp/MemoryAnalyzer-1.9.1.20190826-win32.win32.x86_64.zip
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。