1、背景
java進(jìn)程執(zhí)行特定作業(yè)出現(xiàn)內(nèi)存溢出,跑很多類似作業(yè)是正常的。
2、常見問(wèn)題及原因分析
當(dāng)然這個(gè)問(wèn)題不難,主要是借助這個(gè)機(jī)會(huì)來(lái)一起整理下之前出現(xiàn)過(guò)的問(wèn)題,希望能舉一反三。
2.1 問(wèn)題場(chǎng)景一
Exception in thread thread_name: java.lang.OutOfMemoryError: Java heap space
一般會(huì)有三種情況導(dǎo)致:
堆內(nèi)存太小,無(wú)法滿足應(yīng)用的需 要。
內(nèi)存泄漏,泄漏的內(nèi)存被hold住,無(wú)法使用。
-
Finalizer線程清理實(shí)現(xiàn)了finalize()方法的對(duì)象速度慢于生成這些對(duì)象的速度。
比如在main線程中,創(chuàng)建大量的實(shí)現(xiàn)了finalize()的類對(duì)象,由于main線程優(yōu)先級(jí)大于Finalizer線程,所以main有更多的cpu執(zhí)行時(shí)間,導(dǎo)致對(duì)象生成速度大于清除速度。
class Finalizable { @Override protected void finalize() throws Throwable { try { this.wait(0); } catch(Exception e) { System.err.println(e); } } public static void main(String args[]) { //僅僅為了說(shuō)明問(wèn)題 while(true) { Finalizable f = new Finalizable(); } } }
2.2 問(wèn)題場(chǎng)景二
Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded
gc的內(nèi)存太小,大部分時(shí)間都在gc(98%),且回收的堆內(nèi)存不到2%,且持續(xù)fullgc到了第5次。存活對(duì)象無(wú)法裝入heap中,一般需要加大heap。
2.3 問(wèn)題場(chǎng)景三
Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit
數(shù)組對(duì)象太大, 無(wú)法載入堆內(nèi)存中,原因可能是heap太小或者程序不經(jīng)意產(chǎn)生了大數(shù)組對(duì)象,比較好排查。
2.4 問(wèn)題場(chǎng)景四
Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace
分配給java類元數(shù)據(jù)的本地內(nèi)存用盡通過(guò)增加MaxMetaSpaceSize,或者減少堆內(nèi)存解決。
2.5 問(wèn)題場(chǎng)景五
Exception in thread thread_name: java.lang.OutOfMemoryError: Compressed class space
簡(jiǎn)單理解就是說(shuō),在64位jvm出現(xiàn)之后,用于操作內(nèi)存的OOPS(原始指針)變成了64位。為了避免OOPS擴(kuò)大一倍帶來(lái)的性能損耗,使用UseCompressedOops參數(shù)可以用32位的指針來(lái)指向一個(gè)內(nèi)存空間。如果使用率參數(shù)UseCompressedClassPointers,那么也可以用32位的指針來(lái)指向操作的內(nèi)存區(qū)域,這些指針指向的區(qū)域被叫做“Compressed class space”。如果引入太多包,可能會(huì)超過(guò)Compressed class space導(dǎo)致出現(xiàn)該問(wèn)題,一般調(diào)大Compressed class space就好。
2.6 問(wèn)題場(chǎng)景六
Exception in thread thread_name: java.lang.OutOfMemoryError: reason stack_trace_with_native_method
本地方法內(nèi)存分配失敗,一般通過(guò)OS工具來(lái)定位問(wèn)題。
2.7 問(wèn)題場(chǎng)景七
Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace
Metadata用于保存jvm的靜態(tài)數(shù)據(jù)(如類和方法),分配給java類元數(shù)據(jù)的內(nèi)存用完,比如你的應(yīng)用加載很多類容易出現(xiàn)該問(wèn)題。通過(guò)增加MaxMetaSpaceSize,或者減少堆內(nèi)存解決。
總之,最后定位的問(wèn)題是第一種類型,也就是heap不夠,現(xiàn)在看下如何排查問(wèn)題。
3. 問(wèn)題定位
1、查看jvm的信息
jinfo -flags {pid}
發(fā)現(xiàn)堆內(nèi)存的大小很小,只有128M。
2、導(dǎo)出進(jìn)程的堆內(nèi)存信息。
添加運(yùn)行的JVM參數(shù)(短駐進(jìn)程)
XX:+HeapDumpOnOutOfMemeoryError
XX:HeapDumpPath=/tmp/heap_error.gc
3、MAT分析堆內(nèi)存
發(fā)現(xiàn)ArrayList中占用的內(nèi)存超過(guò)70%,因此分析是存在批處理或者循環(huán)或者內(nèi)存泄漏。進(jìn)一步分析發(fā)現(xiàn)是批處理數(shù)據(jù)量太大(10000),并且每條數(shù)據(jù)量是文本數(shù)據(jù),大對(duì)象太多直接導(dǎo)致堆塞滿。
4、解決
- 設(shè)置合理的批處理量
- 設(shè)置更大的堆
參考
https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html
https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html