《線上linux系統(tǒng)故障排查之一:CPU使用率過(guò)高》
《線上linux系統(tǒng)故障排查之二:內(nèi)存占用過(guò)高》
前一篇文章介紹了《線上linux系統(tǒng)故障排查之一:CPU使用率過(guò)高》的解決方案磕秤,這篇主要分析系統(tǒng)內(nèi)存占用過(guò)高的排查方法缘揪。
在Java開(kāi)發(fā)中捍靠,對(duì)經(jīng)逞勐耍看到這兩種異常:
- java.lang.OutOfMemoryError: PermGen space
- java.lang.OutOfMemoryError: Java heap space
一弦悉、異常出現(xiàn)的原因
1.Java.lang.OutOfMemoryError: PermGen space
PermGen space全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域, 這塊內(nèi)存主要是被JVM存放Class和Meta信息的,Class在被Loader時(shí)就會(huì)被放到PermGen space中, 它和存放類實(shí)例(Instance)的Heap區(qū)域不同,GC(Garbage Collection)不會(huì)在主程序運(yùn)行期對(duì) PermGen space進(jìn)行清理樟澜,所以如果你的應(yīng)用中有很多CLASS的話,就很可能出現(xiàn)PermGen space錯(cuò)誤。如果你的WEB或者APP用了大量的第三方j(luò)ar, 其大小超過(guò)了jvm默認(rèn)的大小(4M)那么就會(huì)產(chǎn)生此錯(cuò)誤信息碉京。
解決方法:
1.調(diào)整PermSize、MaxPermSize的大忻睢谐宙;
2.減少jar重復(fù)使用,重復(fù)占用內(nèi)存界弧。
2.java.lang.OutOfMemoryError: Java heap space
Heap size 設(shè)置 JVM堆的設(shè)置是指java程序運(yùn)行過(guò)程中JVM可以調(diào)配使用的內(nèi)存空間的設(shè)置.JVM在啟動(dòng)的時(shí)候會(huì)自動(dòng)設(shè)置Heap size的值凡蜻,其初始空間(即-Xms)是物理內(nèi)存的1/64,最大空間(-Xmx)是物理內(nèi)存的1/4垢箕』ǎ可以利用JVM提供的-Xmn -Xms -Xmx等選項(xiàng)可進(jìn)行設(shè)置。Heap size 的大小是Young Generation 和Tenured Generaion 之和条获。
在JVM中忠荞,如果98%的時(shí)間是用于GC且可用的Heap size 不足2%的時(shí)候?qū)伋龃水惓P畔ⅰL崾荆篐eap Size 最大不要超過(guò)可用物理內(nèi)存的80%帅掘,一般的要將-Xms和-Xmx選項(xiàng)設(shè)置為相同委煤,而-Xmn為1/4的-Xmx值。
二修档、異常原因排查步驟
1.通過(guò)jstat命令查詢gc情況
通過(guò)top命令定位到內(nèi)存占用過(guò)高的進(jìn)程PID后碧绞,排查該進(jìn)程的GC情況,
命令:jstat -gccause 41843 2000
- 每間隔2s查詢進(jìn)程pid = 41843 的gc情況吱窝;
- gccause表示輸出-gcutil提供的信息以及最后一次執(zhí)行GC的發(fā)生原因和當(dāng)前所執(zhí)行的GC的發(fā)生原因讥邻。
2.通過(guò)jmap命令查詢進(jìn)程實(shí)體類內(nèi)存占用情況
如果步驟1中發(fā)現(xiàn),gc非常頻繁院峡,則可以使用jmap命令查詢進(jìn)程實(shí)體類內(nèi)存占用情況计维。
命令: jmap -histo:live 41843 | head -n 100
- 查詢進(jìn)程pid = 41843 存活的對(duì)象占用內(nèi)存前100排序。
如上圖可以看出撕予,jdbc所占用的存活對(duì)象特別多鲫惶,而且占用的內(nèi)存也很高,從這里就可以看出实抡,需要去檢查下mysql的調(diào)用中欠母,哪里存在問(wèn)題,有內(nèi)存泄露吆寨。
3.通過(guò)jmap命令查詢進(jìn)程堆的使用情況
如果以上沒(méi)有查出問(wèn)題赏淌,可以看看進(jìn)程中,新生代啄清、老年代六水、永久代的使用情況。
命令: jmap -heap 41843
如上圖,如果發(fā)現(xiàn)頻繁的gc是因?yàn)樾律兰帧⒗夏甏﹂⒂谰么峙涞拇笮∮袉?wèn)題,則可以通過(guò)修改設(shè)置解決想帅。
永久代解決方法(同上):
1.調(diào)整PermSize场靴、MaxPermSize的大小港准;
2.減少jar重復(fù)使用旨剥,重復(fù)占用內(nèi)存。
新生代浅缸、老年代解決方法:
1.調(diào)整Xms -Xmx -Xmn的大小
示例及參數(shù)注解:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:PermSize=64M -XX:MaxPermSize=128M -XX:MaxTenuringThreshold=0
-Xmx3550m:設(shè)置JVM最大可用內(nèi)存為3550M轨帜。
-Xms3550m:設(shè)置JVM促使內(nèi)存為3550m。此值可以設(shè)置與-Xmx相同衩椒,以避免每次垃圾回收完成后JVM重新分配內(nèi)存蚌父;
-Xmn2g:設(shè)置年輕代大小為2G。整個(gè)JVM內(nèi)存大小=年輕代大小 + 年老代大小 + 持久代大小烟具。持久代一般固定大小為64m,所以增大年輕代后奠蹬,將會(huì)減小年老代大小朝聋。此值對(duì)系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8囤躁;
-Xss128k:設(shè)置每個(gè)線程的堆棧大小冀痕。JDK5.0以后每個(gè)線程堆棧大小為1M,以前每個(gè)線程堆棧大小為256K狸演。更具應(yīng)用的線程所需內(nèi)存大小進(jìn)行調(diào)整言蛇。在相同物理內(nèi)存下,減小這個(gè)值能生成更多的線程宵距。但是操作系統(tǒng)對(duì)一個(gè)進(jìn)程內(nèi)的線程數(shù)還是有限制的腊尚,不能無(wú)限生成,經(jīng)驗(yàn)值在3000~5000左右满哪;
-XX:NewRatio=4:設(shè)置年輕代(包括Eden和兩個(gè)Survivor區(qū))與年老代的比值(除去持久代)婿斥。設(shè)置為4,則年輕代與年老代所占比值為1:4哨鸭,年輕代占整個(gè)堆棧的1/5民宿;
-XX:SurvivorRatio=4:設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的大小比值。設(shè)置為4像鸡,則兩個(gè)Survivor區(qū)與一個(gè)Eden區(qū)的比值為2:4活鹰,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/6;
-XX:PermSize=64M JVM初始分配的非堆內(nèi)存(永久代);
-XX:MaxPermSize=128M JVM最大允許分配的非堆內(nèi)存志群,按需分配着绷;
-XX:MaxTenuringThreshold=0:設(shè)置垃圾最大年齡。如果設(shè)置為0的話赖舟,則年輕代對(duì)象不經(jīng)過(guò)Survivor區(qū)蓬戚,直接進(jìn)入年老代。對(duì)于年老代比較多的應(yīng)用宾抓,可以提高效率子漩。如果將此值設(shè)置為一個(gè)較大值,則年輕代對(duì)象會(huì)在Survivor區(qū)進(jìn)行多次復(fù)制石洗,這樣可以增加對(duì)象再年輕代的存活時(shí)間幢泼,增加在年輕代即被回收的概論。