中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

網(wǎng)站設(shè)計公司 上seo賺錢方法大揭秘

網(wǎng)站設(shè)計公司 上,seo賺錢方法大揭秘,微信crm系統(tǒng)哪家好,鄭州做網(wǎng)站建設(shè)公司大綱 1.如何對系統(tǒng)的OOM異常進行監(jiān)控和報警 2.如何在JVM內(nèi)存溢出時自動dump內(nèi)存快照 3.Metaspace區(qū)域內(nèi)存溢出時應(yīng)如何解決(OutOfMemoryError: Metaspace) 4.JVM棧內(nèi)存溢出時應(yīng)如何解決(StackOverflowError) 5.JVM堆內(nèi)存溢出時應(yīng)該如何解決(OutOfMemoryError: Java heap s…

大綱

1.如何對系統(tǒng)的OOM異常進行監(jiān)控和報警

2.如何在JVM內(nèi)存溢出時自動dump內(nèi)存快照

3.Metaspace區(qū)域內(nèi)存溢出時應(yīng)如何解決(OutOfMemoryError: Metaspace)

4.JVM棧內(nèi)存溢出時應(yīng)如何解決(StackOverflowError)

5.JVM堆內(nèi)存溢出時應(yīng)該如何解決(OutOfMemoryError: Java heap space)

1.如何對系統(tǒng)的OOM異常進行監(jiān)控和報警

(1)最佳的解決方案

(2)搭建系統(tǒng)監(jiān)控體系的建議

(1)最佳的解決方案

最佳的OOM監(jiān)控方案就是:建立一套監(jiān)控平臺,比如搭建Zabbix、Open-Falcon之類的監(jiān)控平臺。如果有監(jiān)控平臺,就可以接入系統(tǒng)異常的監(jiān)控和報警,可以設(shè)置當系統(tǒng)出現(xiàn)OOM異常,就發(fā)送報警給對應(yīng)的開發(fā)人員。這是中大型公司里最常用的一種方案。

監(jiān)控平臺一般會監(jiān)控系統(tǒng)的如下幾個層面:

一.機器資源(CPU、磁盤、內(nèi)存、網(wǎng)絡(luò))的負載

二.JVM的GC頻率和內(nèi)存使用率

三.系統(tǒng)自身的業(yè)務(wù)指標

四.系統(tǒng)的異常報錯

(2)搭建系統(tǒng)監(jiān)控體系的建議

建議一:對機器指標進行監(jiān)控

一.首先可以看到機器的資源負載情況

比如CPU負載,可以看到現(xiàn)在CPU目前的使用率有多高。比如磁盤IO負載,包括磁盤上發(fā)生了多少數(shù)據(jù)量的IO、一些IO耗時等。當然一般業(yè)務(wù)系統(tǒng)不會直接讀寫本地磁盤,最多就是寫一些本地日志。但是一般業(yè)務(wù)系統(tǒng)也應(yīng)該關(guān)注本地磁盤的使用量和剩余空間,因為有的系統(tǒng)可能會因為一些代碼bug,導(dǎo)致一直往本地磁盤寫東西。萬一把磁盤空間寫滿了就麻煩了,磁盤滿了也會導(dǎo)致系統(tǒng)無法運行。

二.其次可以看到機器的內(nèi)存使用量

這個是從機器整體層面去看的,看看機器對內(nèi)存使用的一些變化。當然內(nèi)存這塊,比較核心的還是JVM的監(jiān)控,通過監(jiān)控平臺可以看到JVM各個內(nèi)存區(qū)域的使用量的變化的。

三.還有一個比較關(guān)鍵的是JVM的FGC頻率

一般會監(jiān)控一段時間內(nèi)的FGC次數(shù),比如5分鐘內(nèi)發(fā)生了幾次FGC。

四.最后就是機器上的網(wǎng)絡(luò)負載

就是通過網(wǎng)絡(luò)IO讀寫了多少數(shù)據(jù)、一些耗時等。

其實線上機器最容易出問題的主要有三方面:

一是CPU(必須要監(jiān)控CPU的使用率),如果CPU負載過高(如長期超過90%)就報警。

二是內(nèi)存(必須要監(jiān)控內(nèi)存的使用率),如果機器內(nèi)存使用率超過一定閾值(如超90%)則可能內(nèi)存不夠。

三是JVM的FGC頻率,假設(shè)5分鐘內(nèi)發(fā)生了10次FGC,那一定是頻繁FGC了。

建議二:對業(yè)務(wù)指標進行監(jiān)控

另外比較常見的就是對系統(tǒng)的業(yè)務(wù)指標進行監(jiān)控。比如在系統(tǒng)每創(chuàng)建一個訂單時就上報一次監(jiān)控,然后監(jiān)控系統(tǒng)會收集1分鐘內(nèi)的訂單數(shù)量。這樣就可以設(shè)定一個閾值,比如1分內(nèi)要是訂單數(shù)超過100就報警。因為訂單過多可能涉及一些刷單行為,這就是業(yè)務(wù)指標監(jiān)控。

建議三:對系統(tǒng)異常進行監(jiān)控

最后就是對系統(tǒng)中所有的try catch異常報錯,都接入報警。一旦發(fā)現(xiàn)有try catch異常,就上報到監(jiān)控平臺。然后監(jiān)控平臺就能通告這次異常,讓相關(guān)負責人收到報警。比如一旦發(fā)現(xiàn)有OOM異常,就能馬上通知相關(guān)開發(fā)人員。

2.如何在JVM內(nèi)存溢出時自動dump內(nèi)存快照

(1)解決OOM問題的一個初步思路

(2)在OOM時自動dump內(nèi)存快照

(3)一份比較全面的JVM參數(shù)模板

(1)解決OOM問題的一個初步思路

如果發(fā)生OOM,則說明系統(tǒng)中某個區(qū)域的對象太多,塞滿了那個區(qū)域。而且一定是無法回收掉區(qū)域中的那些對象,最終才會導(dǎo)致內(nèi)存溢出。

要解決OOM,首先得知道是什么對象太多了,從而最終導(dǎo)致OOM的。所以必須有一份JVM發(fā)生OOM時的dump內(nèi)存快照,只要有了那個dump內(nèi)存快照,就可以用MAT工具分析什么對象太多了。

那么現(xiàn)在一個關(guān)鍵問題來了:到底怎么做才可以在JVM內(nèi)存溢出時自動dump出一份內(nèi)存快照呢?

(2)在OOM時自動dump內(nèi)存快照

如果JVM發(fā)生OOM,JVM是不是完全來不及處理然后突然進程就沒了?也就是JVM是不是非常突然的、自己都無法控制,就掛掉了?

其實不是的,JVM在發(fā)生OOM之前會盡可能進行GC騰出一些內(nèi)存空間。如果GC后還是沒有空間,放不下對象,?才會觸發(fā)內(nèi)存溢出。所以JVM自己對OOM情況的發(fā)生是完全有把控權(quán)的。

JVM知道什么時候會觸發(fā)OOM,它是在無法放下對象的時候才會觸發(fā)的。因此OOM的發(fā)生并不是突然內(nèi)存太多,連JVM自己都沒反應(yīng)過來就崩潰。

JVM如果知道自己將要發(fā)生OOM了,那么此時完全可以讓它做點事情。比如可以讓JVM在OOM時dump一份內(nèi)存快照,事后只要分析這個內(nèi)存快照,就可以知道是哪些對象導(dǎo)致OOM的了。為此,需要在JVM的啟動參數(shù)中加入如下參數(shù):

?-XX:+HeapDumpOnOutOfMemoryError???-XX:HeapDumpPath=/usr/local/app/oom

第一個參數(shù)的意思是:在OOM時自動進行dump內(nèi)存快照。第二個參數(shù)的意思是:把內(nèi)存快照放到哪里。只要加入了這兩個參數(shù),可以事后再獲取OOM時的內(nèi)存快照進行分析。

(3)一份比較全面的JVM參數(shù)模板???????

?-Xms4096M -Xmx4096M -Xmn3072M -Xss1M??-XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M?-XX:+UseParNewGC?-XX:+UseConcMarkSweepGC??-XX:CMSInitiatingOccupancyFaction=92??-XX:+UseCMSCompactAtFullCollection?-XX:CMSFullGCsBeforeCompaction=0??-XX:+CMSParallelInitialMarkEnabled?-XX:+CMSScavengeBeforeRemark??-XX:+DisableExplicitGC??-XX:+PrintGCDetails?-Xloggc:gc.log??-XX:+HeapDumpOnOutOfMemoryError?-XX:HeapDumpPath=/usr/local/app/oom

這份JVM參數(shù)模板基本上涵蓋了所有需要的一些參數(shù)。首先是各個內(nèi)存區(qū)域的大小分配,這個需要精心調(diào)優(yōu)的。其次是兩種垃圾回收器的指定。接著是一些常規(guī)的CMS垃圾回收參數(shù),可優(yōu)化偶爾發(fā)生的FGC性能。最重要的,就是平時要打印出GC日志。GC日志可以配合jstat工具分析GC頻率和性能的時候使用。jstat可分析出GC頻率,但每次具體的GC情況則要結(jié)合GC日志來看。還有次重要的,就是發(fā)生OOM時能自動dump內(nèi)存快照。這樣即使突然發(fā)生OOM,且事后才知道,都可以分析當時的內(nèi)存快照。

3.Metaspace區(qū)域內(nèi)存溢出時應(yīng)如何解決(OutOfMemoryError: Metaspace)

(1)解決思路

(2)示例代碼

(3)分析GC日志

(4)分析內(nèi)存快照

(1)解決思路

面對Metaspace區(qū)域內(nèi)存溢出,思路如下:首先分析GC日志,然后再讓JVM自動dump出內(nèi)存快照。最后用MAT來分析這份內(nèi)存快照,從內(nèi)存快照里尋找OOM的原因。

(2)示例代碼???????

public?class?Demo1?{public?static?void?main(String[] args)?{long?counter =?0;while?(true) {Enhancer enhancer =?new?Enhancer();enhancer.setSuperclass(Car.class);enhancer.setUseCache(false);enhancer.setCallback(new?MethodInterceptor() {@Overridepublic?Object?intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable?{if?(method.getName().equals("run")) {System.out.println("Check before run");return?methodProxy.invokeSuper(o, objects);}?else?{return?methodProxy.invokeSuper(o, objects);}}});Car car = (Car) enhancer.create();car.run();System.out.println("目前創(chuàng)建了"?+ (++counter) +?"個Car類的子類了");}}static?class?Car?{public?void?run()?{System.out.println("Run...");}}static?class?SafeCar?extends?Car?{@Overridepublic?void?run()?{System.out.println("Safe Run...");super.run();}}
}

接著JVM參數(shù)修改為如下,因為我們想看一下GC日志和導(dǎo)出內(nèi)存快照。???????

?-XX:+UseParNewGC?-XX:+UseConcMarkSweepGC?-XX:MetaspaceSize=10m?-XX:MaxMetaspaceSize=10m?-XX:+PrintGCDetails?-Xloggc:gc.log?-XX:+HeapDumpOnOutOfMemoryError?-XX:HeapDumpPath=./

注意,上面那個HeapDumpPath參數(shù)調(diào)整為當前項目下,這樣方便查看。

(3)分析GC日志

接下來用上述JVM參數(shù)運行這段程序,會發(fā)現(xiàn)項目下多了兩個文件:一個是gc.log,一個是java_pid910.hprof。當然不同的機器運行這個程序,導(dǎo)出的hprof文件的名字是不太一樣的,因為hprof文件會用PID進程id作為文件名字。

步驟一:首先分析gc.log

分析它是如何不斷往Metaspace區(qū)放入大量生成的類,然后觸發(fā)FGC的。接著回收Metaspace區(qū),回收后還是無法放下更多類,才拋出OOM。

步驟二:然后用MAT分析OOM時的內(nèi)存快照

也就是使用MAT工具找到Metaspace內(nèi)存溢出問題的原因,GC日志如下:???????

CommandLine?flags:?-XX:CompressedClassSpaceSize=2097152?-XX:+HeapDumpOnOutOfMemoryError?-XX:HeapDumpPath=./ -XX:InitialHeapSize=268435456?-XX:MaxHeapSize=4294967296?-XX:MaxMetaspaceSize=10485760?-XX:MaxNewSize=348966912?-XX:MaxTenuringThreshold=6?-XX:MetaspaceSize=10485760?-XX:OldPLABSize=16?-XX:+PrintGC?-XX:+PrintGCDetails?-XX:+PrintGCTimeStamps?-XX:+UseCompressedClassPointers?-XX:+UseCompressedOops?-XX:+UseConcMarkSweepGC?-XX:+UseParNewGC?
0.716: [GC?(Allocation?Failure)?0.717: [ParNew: 139776K->2677K(157248K),?0.0038770?secs] 139776K->2677K(506816K),?0.0041376?secs] [Times: user=0.03?sys=0.01, real=0.00?secs]
0.771: [Full?GC?(Metadata?GC?Threshold)?0.771: [CMS: 0K->2161K(349568K),?0.0721349?secs] 20290K->2161K(506816K), [Metaspace: 9201K->9201K(1058816K)],?0.0722612?secs] [Times: user=0.12?sys=0.03, real=0.08?secs]
0.843: [Full?GC?(Last?ditch collection)?0.843: [CMS: 2161K->1217K(349568K),?0.0164047?secs] 2161K->1217K(506944K), [Metaspace: 9201K->9201K(1058816K)],?0.0165055?secs] [Times: user=0.03?sys=0.00, real=0.01?secs]
0.860: [GC?(CMS?Initial?Mark) [1?CMS-initial-mark:?1217K(349568K)] 1217K(506944K),?0.0002251?secs] [Times: user=0.00?sys=0.00, real=0.00?secs]
0.860: [CMS-concurrent-mark-start]
0.878: [CMS-concurrent-mark:?0.003/0.018?secs] [Times: user=0.05?sys=0.01, real=0.02?secs]
0.878: [CMS-concurrent-preclean-start]
Heappar new generation ? total 157376K, used 6183K [0x00000005ffe00000,?0x000000060a8c0000,?0x0000000643790000)eden space 139904K, ??4% used [0x00000005ffe00000,?0x0000000600409d48,?0x00000006086a0000)from space 17472K, ??0% used [0x00000006086a0000,?0x00000006086a0000,?0x00000006097b0000)to ? space 17472K, ??0% used [0x00000006097b0000,?0x00000006097b0000,?0x000000060a8c0000)concurrent mark-sweep generation total 349568K, used 1217K [0x0000000643790000,?0x0000000658cf0000,?0x00000007ffe00000)
Metaspace? ? ? ?used 9229K, capacity 10146K, committed 10240K, reserved 1058816Kclass?space ? ?used 794K, capacity 841K, committed 896K, reserved 1048576K

一.第一行GC日志如下

0.716: [GC (Allocation Failure)?0.717: [ParNew:?139776K->2677K(157248K),?0.0038770?secs]?139776K->2677K(506816K),?0.0041376?secs] [Times: user=0.03?sys=0.01, real=0.00?secs]

這行日志,這是第一次GC,它本身是一個Allocation Failure的問題。也就是說,在Eden區(qū)中分配對象時,已經(jīng)發(fā)現(xiàn)Eden區(qū)內(nèi)存不足了,于是就觸發(fā)了一次YGC。

二.那么這個對象到底是什么對象

回到代碼中,可知Enhancer是一個對象,它是用來生成類的,如下所示:

Enhancer?enhancer?=?new?Enhancer();

接著會基于new Enhancer生成類對象來生成那個子類的對象,如下所示:

Car?car?=?(Car) enhancer.create();

上述代碼會在while(true)里不停創(chuàng)建Enhancer對象和Car的子類對象,因此Eden區(qū)慢慢就會被占滿了,于是會看到上述日志:

[ParNew: 139776K->2677K(157248K), 0.0038770 secs]?

這行日志就是說:在默認的內(nèi)存分配策略下,新生代一共可用空間是150M左右。然后大概用到140M,Eden區(qū)都占滿了,就會觸發(fā)Allocation Failure。即沒有Eden區(qū)的空間去分配對象了,此時就只能觸發(fā)YGC。

三.接著來看如下GC日志

0.771: [Full GC (Metadata GC Threshold)?0.771: [CMS:?0K->2161K(349568K),?0.0721349?secs]?20290K->2161K(506816K), [Metaspace:?9201K->9201K(1058816K)],?0.0722612?secs] [Times: user=0.12?sys=0.03, real=0.08?secs]

這行日志說明就是發(fā)生FGC了,而且通過Metadata GC Threshold清晰看到,是Metaspace區(qū)域滿了,這時看后面的日志:

20290K->2161K(506816K);

這就是說堆內(nèi)存(新生代+老年代)一共是500M,有20M被使用了,這20M的內(nèi)存是被新生代使用的。

然后FGC必然會帶著一次YGC,因此這次FGC執(zhí)行了YGC,所以回收了很多對象,剩下2161K的對象,這2161K的對象大概就是JVM的一些內(nèi)置對象。

然后直接就把這些對象都放入老年代,為什么呢?因為后面的日志:

[CMS: 0K->2161K(349568K), 0.0721349 secs]

明顯表明,FGC帶著CMS進行了老年代的Old GC,結(jié)果人家本來是0K。然后從新生代轉(zhuǎn)移來了2161K的對象,所以老年代變成2161K了。

接著看日志:?

[Metaspace: 9201K->9201K(1058816K)]

這說明此時Metaspace區(qū)域已經(jīng)使用了差不多9M左右的內(nèi)存了。JVM發(fā)現(xiàn)離限制的10M內(nèi)存很接近了,于是觸發(fā)了FGC。但是對Metaspace進行GC后發(fā)現(xiàn)類的對象全部都還存活,因此還是剩余9M左右的類在Metaspace里。

四.接著來看下一行GC日志

0.843: [Full?GC (Last?ditch collection)?0.843: [CMS:?2161K->1217K(349568K),?0.0164047?secs]?2161K->1217K(506944K), [Metaspace:?9201K->9201K(1058816K)],?0.0165055?secs] [Times:?user=0.03?sys=0.00,?real=0.01?secs]

接著又是一次FGC。其中的Last ditch collection,說明這是最后一次拯救的機會了。因為之前Metaspace回收了一次但發(fā)現(xiàn)沒有類可以回收,所以新的類無法放入Metaspace了。因此才再最后試一試FGC,看看能不能回收掉一些。

結(jié)果發(fā)現(xiàn)還是:

[Metaspace: 9201K->9201K(1058816K)],?0.0165055?secs]

這說明Metaspace區(qū)域還是無法回收掉任何類,Metaspace區(qū)幾乎還是占滿了設(shè)置的10M內(nèi)存。

五.繼續(xù)看如下GC日志???????

0.860: [GC (CMS?Initial?Mark) [1?CMS-initial-mark:?1217K(349568K)]?1217K(506944K),?0.0002251?secs] [Times:?user=0.00?sys=0.00,?real=0.00?secs]
0.860: [CMS-concurrent-mark-start]
0.878: [CMS-concurrent-mark:?0.003/0.018?secs] [Times:?user=0.05?sys=0.01,?real=0.02?secs]
0.878: [CMS-concurrent-preclean-start]
Heappar?new?generation ? total?157376K, used?6183K [0x00000005ffe00000,?0x000000060a8c0000,?0x0000000643790000)eden space?139904K, ??4%?used [0x00000005ffe00000,?0x0000000600409d48,?0x00000006086a0000)from?space?17472K, ??0%?used [0x00000006086a0000,?0x00000006086a0000,?0x00000006097b0000)to? ?space?17472K, ??0%?used [0x00000006097b0000,?0x00000006097b0000,?0x000000060a8c0000)concurrent mark-sweep generation total?349568K, used?1217K [0x0000000643790000,?0x0000000658cf0000,?0x00000007ffe00000)
Metaspace ? ? ? used?9229K, capacity?10146K, committed?10240K, reserved?1058816Kclass space ? ?used?794K, capacity?841K, committed?896K, reserved?1048576K

接著JVM就直接退出了,退出的時候打印了當前內(nèi)存的一個情況:年輕代和老年代幾乎沒占用。但是Metaspace的capacity是10M,使用了9M左右。無法再繼續(xù)使用了,所以觸發(fā)了內(nèi)存溢出。

此時在控制臺會打印出如下的信息:???????

Caused by: java.lang.OutOfMemoryError: Metaspace? ? at java.lang.ClassLoader.defineClass1(Native Method)? ? at java.lang.ClassLoader.defineClass(ClassLoader.java:763)? ? ...?11?more

明確拋出異常表明OutOfMemoryError,原因就是Metaspace區(qū)域滿了。

因此可以假設(shè)是Metaspace內(nèi)存溢出了,然后我們自己監(jiān)控到了異常。此時直接去線上機器看一下GC日志和異常信息。通過上述分析就能知道系統(tǒng)是如何運行、觸發(fā)幾次GC后引發(fā)內(nèi)存溢出。

(4)分析內(nèi)存快照

當我們知道是Metaspace引發(fā)的內(nèi)存溢出后:就把內(nèi)存快照文件從線上機器拷回本地電腦,打開MAT工具進行分析。如下圖示:

圖片

從上圖可以看到實例最多的就是AppClassLoader。為什么會有這么多的ClassLoader呢?一看就是CGLIB之類的東西在動態(tài)生成類的時候搞出來的,此時我們可以點擊上圖的Details進去看看。

圖片

為什么有一大堆在Demo1中動態(tài)生成的Car$$EnhancerByCGLIB類?看到截圖中的Object數(shù)組里出現(xiàn)的很多這種類,就已經(jīng)很清晰說明了:某個類生成了大量動態(tài)類EnhancerByCGLIB,填滿了Metaspace區(qū)。所以此時直接去代碼里排查動態(tài)生成類的代碼即可。

解決這個問題的辦法也很簡單:對Enhancer進行緩存,只需要一個Enhancer實例即可,不需要無限制地生成類。

4.JVM棧內(nèi)存溢出時應(yīng)如何解決(StackOverflowError)

(1)棧內(nèi)存溢出能否按照之前的方法解決

(2)代碼示例

(3)運行代碼后分析異常報錯信息的調(diào)用棧

(1)棧內(nèi)存溢出能否按照之前的方法解決

也就是說,GC日志、內(nèi)存快照,這些東西對解決棧內(nèi)存溢出有幫助嗎?其實棧內(nèi)存溢出跟堆內(nèi)存是沒有關(guān)系的,因為它的本質(zhì)是一個線程的棧中壓入了過多調(diào)用方法的棧楨。比如調(diào)用了幾千次方法就會壓入幾千個方法棧楨,此時就會導(dǎo)致線程的棧內(nèi)存不足,無法放入更多棧楨。

GC日志對棧內(nèi)存溢出是沒有用的,因為GC日志主要分析的是堆內(nèi)存和Metaspace區(qū)域的一些GC情況。就線程的棧內(nèi)存和棧楨而言,不存在所謂的GC。

線程調(diào)用一個方法時,會在線程的虛擬機棧里壓入方法棧楨。接著線程執(zhí)行完該方法后,方法棧楨會從線程的虛擬機棧里出棧。最后一個線程運行完畢時,它的虛擬機棧內(nèi)存就會被釋放。

所以本身棧內(nèi)存不存在所謂的GC和回收。線程調(diào)用方法時就會給方法棧楨分配內(nèi)存,線程執(zhí)行完方法時就會回收掉那個方法棧楨的內(nèi)存。

內(nèi)存快照是分析內(nèi)存占用的,同樣是針對堆內(nèi)存和Metaspace的。所以對線程的棧內(nèi)存而言,也不需要使用內(nèi)存快照。

(2)代碼示例???????

public?class?Demo2?{? ??public?static?long?counter =?0;? ??public?static?void?main(String[] args)?{? ? ? ? work();? ? }? ??public?static?void?work()?{? ? ? ? System.out.println("目前是第"?+ (++counter) +?"次調(diào)用方法");? ? ? ? work();? ? }}

使用的JVM參數(shù)如下:???????

?-XX:+UseParNewGC?-XX:+UseConcMarkSweepGC?-XX:ThreadStackSize=1m??-XX:+PrintGCDetails?-Xloggc:gc.log?-XX:+HeapDumpOnOutOfMemoryError?-XX:HeapDumpPath=./

(3)運行代碼后分析異常報錯信息的調(diào)用棧

接著運行代碼產(chǎn)生棧內(nèi)存溢出,如下:???????

目前是第5549次調(diào)用方法java.lang.StackOverflowError? at java.io.FileOutputStream.write(FileOutputStream.java:326)? at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)? at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)? at java.io.PrintStream.write(PrintStream.java:482)? at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)? at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)? at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)? at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)? at java.io.PrintStream.write(PrintStream.java:527)? at java.io.PrintStream.print(PrintStream.java:669)? at java.io.PrintStream.println(PrintStream.java:806)? at com.demo.rpc.test.Demo.work(Demo.java:9)? at com.demo.rpc.test.Demo.work(Demo.java:10)

上述所示異常會明確提示:出現(xiàn)棧內(nèi)存溢出的問題,原因是不斷調(diào)用Demo類的work()方法導(dǎo)致的。

因此對于棧內(nèi)存溢出的問題,定位和解決的思路就很簡單了。只要把所有的異常都寫入本地日志文件,那么發(fā)現(xiàn)系統(tǒng)崩潰時,去日志里定位異常信息即可。

(4)總結(jié)

情形一:Metaspace區(qū)域溢出

通過異常信息可以直接定位出是Metaspace區(qū)域發(fā)生異常,然后分析GC日志就可以知道Metaspace發(fā)生溢出的全過程,接著再使用MAT分析內(nèi)存快照,就知道是哪個類太多導(dǎo)致異常。

情形二:棧內(nèi)存溢出

首先從異常日志中就能知道是棧內(nèi)存溢出。

然后從異常日志中可以找到對應(yīng)的報錯方法。

知道哪個方法后,就可以到代碼中定位問題。

5.JVM堆內(nèi)存溢出時應(yīng)該如何解決(OutOfMemoryError: Java heap space)

(1)示例代碼

(2)運行后的觀察

(3)用MAT分析內(nèi)存快照

(4)總結(jié)

(1)示例代碼

運行如下程序:???????

public?class?Demo3?{? ??public?static?void?main(String[] args) {? ? ? ? long counter =?0;? ? ? ??List<Object> list =?new?ArrayList<Object>();? ? ? ??while(true) {? ? ? ? ? ? list.add(new?Object());? ? ? ? ? ??System.out.println("當前創(chuàng)建了第"?+ (++counter) +?"個對象");? ? ? ? } ? ?? ? }}

采用的JVM參數(shù)如下:???????

?-Xms10m?-Xmx10m?-XX:+PrintGCDetails?-Xloggc:gc.log?-XX:+HeapDumpOnOutOfMemoryError?-XX:HeapDumpPath=./?-XX:+UseParNewGC?-XX:+UseConcMarkSweepGC

(2)運行后的觀察

其實堆內(nèi)存溢出的現(xiàn)象也是很簡單的,在系統(tǒng)運行一段時間之后,會發(fā)現(xiàn)系統(tǒng)崩潰了,然后登錄到線上機器檢查日志文件。先看到底為什么崩潰:???????

java.lang.OutOfMemoryError: Java heap spaceDumping heap to ./java_pid1023.hprof ...Heap dump file created [13409210 bytes?in?0.033 secs]? ? Exception?in?thread?"main"?java.lang.OutOfMemoryError: Java heap space

這個就表明了:是Java堆內(nèi)存溢出了,而且還導(dǎo)出了一份內(nèi)存快照。此時GC日志都不用分析,因為堆內(nèi)存溢出往往對應(yīng)著大量的GC日志。這些大量的GC日志分析起來很麻煩,可以直接將線上自動導(dǎo)出的內(nèi)存快照拷貝回本地,然后使用MAT分析。

(3)用MAT分析內(nèi)存快照

采用MAT打開內(nèi)存快照后會看到下圖:

圖片

這次MAT非常簡單,直接在內(nèi)存泄漏報告中告訴我們。內(nèi)存溢出原因只有一個,因為它沒提示任何其他的問題。接下來仔細分析一下MAT提供的分析報告。

一.首先看下面的提示

The thread java.lang.Thread @?0x7bf6a9a98?main keeps local variables?with?total size?7,203,536?(92.03%)?bytes.

意思是main線程通過局部變量引用了7230536個字節(jié)的對象(7M),考慮到總共就給堆內(nèi)存10M,所以7M基本上個已經(jīng)到極限了。

二.接著看下面的提示

The memory?is?accumulated?in?one instance of?"java.lang.Object[]"?loaded?by?"<system class loader>".

這句話的意思是內(nèi)存都被一個實例對象占用了,就是java.lang.Object[]。

三.這時還不能判斷,得點擊Details

圖片

在Details里能看到這個東西,即占用了7M內(nèi)存的的java.lang.Object[]。這里會列出該Object數(shù)組的每個元素,可見是大量的java.lang.Object,而這些java.lang.Object其實就是我們在代碼里創(chuàng)建的。至此真相大白,就是大量的Object對象占用了7M的內(nèi)存導(dǎo)致內(nèi)存溢出。

四.接著分析這些對象是如何創(chuàng)建出來的

此時可以回到上一級頁面進行查找,如下:

圖片

這個意思是可以查看創(chuàng)建那么多對象的線程,它的一個執(zhí)行棧。從線程執(zhí)行棧中就可知這個線程在執(zhí)行什么方法時創(chuàng)建了大量的對象。

圖片

從上面的調(diào)用??梢钥吹?#xff1a;Demo3.main()方法會一直調(diào)用ArrayList.add()方法,然后引發(fā)OOM。所以接下來只要在對應(yīng)代碼里看一下,就知道怎么回事了。接著優(yōu)化對應(yīng)的代碼即可,這樣就不會發(fā)生內(nèi)存溢出了。

(4)總結(jié)

堆內(nèi)存溢出問題的分析和定位:

一是加入自動導(dǎo)出內(nèi)存快照的參數(shù)

二是到線上看一下日志文件里的報錯

如果是堆溢出,則用MAT分析內(nèi)存快照。

MAT分析的時候一些順序和技巧:

一.首先看占用內(nèi)存最多的對象是誰

二.然后分析那個線程的調(diào)用棧

三.接著看哪個方法引發(fā)內(nèi)存溢出

四.最后優(yōu)化代碼即可

http://www.risenshineclean.com/news/5796.html

相關(guān)文章:

  • 裝飾公司怎樣做網(wǎng)站交換友情鏈接的渠道
  • 通達oa 做網(wǎng)站企業(yè)培訓(xùn)權(quán)威機構(gòu)
  • 恐怖小說網(wǎng)站怎么做小廣告模板
  • 新媒體公司網(wǎng)站怎么做2345網(wǎng)址導(dǎo)航瀏覽器下載
  • 仿網(wǎng)站建設(shè)免費b2b網(wǎng)站有哪些
  • 汕頭人才招聘網(wǎng)最新招聘信息北京seo全網(wǎng)營銷
  • 用c 做的網(wǎng)站怎么打開域名免費注冊0元注冊
  • 攝影師簽約有哪些網(wǎng)站線上宣傳渠道
  • 成都娛樂場所關(guān)閉最新消息武漢建站優(yōu)化廠家
  • 中糧網(wǎng)站是哪個公司做的上海高端seo公司
  • 長春二道網(wǎng)站建設(shè)百度明星搜索量排行榜
  • 響應(yīng)式網(wǎng)站wordpress攝影家居seo整站優(yōu)化方案
  • 網(wǎng)站開發(fā)量計算百度一下你就知道移動官網(wǎng)
  • 如何做商業(yè)推廣網(wǎng)站提高關(guān)鍵詞排名的軟文案例
  • 網(wǎng)站站點文件夾權(quán)限設(shè)置青島網(wǎng)站關(guān)鍵詞優(yōu)化公司
  • 中國做網(wǎng)站推廣哪家好武漢外包seo公司
  • 浦東新區(qū)消息今天淘寶seo什么意思
  • 網(wǎng)站怎么做三級的游戲優(yōu)化大師
  • 建行網(wǎng)站登錄不了荊門網(wǎng)絡(luò)推廣
  • 深圳網(wǎng)站制作必選祥奔科技域名訪問網(wǎng)站怎么進入
  • 長沙理財網(wǎng)站建設(shè)魔方優(yōu)化大師官網(wǎng)
  • 網(wǎng)站做推廣頁需要什么軟件注冊公司
  • 在putty上怎樣安裝wordpress刷關(guān)鍵詞優(yōu)化排名
  • 如何給網(wǎng)站做301重定向搜索引擎有哪幾個網(wǎng)站
  • 一個公司做兩個網(wǎng)站的好處seo上海培訓(xùn)
  • 自助建站系統(tǒng)源碼下載大慶網(wǎng)絡(luò)推廣
  • 錦州做網(wǎng)站廣州做網(wǎng)站的公司哪家好
  • 獨立網(wǎng)站優(yōu)化廣告寧波seo網(wǎng)絡(luò)推廣渠道介紹
  • 統(tǒng)一社會信用代碼查詢廈門網(wǎng)站seo哪家好
  • 做網(wǎng)站怎么兼職網(wǎng)絡(luò)公司網(wǎng)絡(luò)營銷推廣方案