摘要:方法區(qū)溢出在的方法區(qū)中,它主要存放了類(lèi)的信息,常量,靜態(tài)變量等。運(yùn)行結(jié)果簡(jiǎn)單解決思路一般來(lái)說(shuō)此類(lèi)問(wèn)題多出現(xiàn)在存在遞歸的地方,要從代碼里重新審視遞歸未結(jié)束的原因,若遞歸的方法沒(méi)問(wèn)題可以根據(jù)實(shí)際情況調(diào)整參數(shù)的大小。
前言
如今不管是在面試還是在我們的工作中,OOM總是不斷的出現(xiàn)在我們的視野中,所以我們有必要去了解一下導(dǎo)致OOM的原因以及一些基本的調(diào)整方法,大家可以通過(guò)下面的事例來(lái)了解一下什么樣的代碼會(huì)導(dǎo)致OOM,幫助我們以后在工作中能夠通過(guò)異常信息來(lái)判斷是JVM里面哪個(gè)區(qū)域出現(xiàn)了問(wèn)題。
先介紹一下筆者的相關(guān)編碼環(huán)境。
jdk:java version "1.8.0_121"
ide:IntelliJ IDEA 2019.1 (Community Edition)
正文Java中的堆存儲(chǔ)的都是對(duì)象實(shí)例,當(dāng)我們不斷的創(chuàng)建對(duì)象,而GC的時(shí)候又不能回收,當(dāng)存儲(chǔ)的對(duì)象大小超過(guò)了-Xmx的值,這時(shí)候則會(huì)出現(xiàn)OutOfMemoryError.[-XX:+HeapDumpOnOutOfMemoryError]參數(shù)可以讓jvm出現(xiàn)內(nèi)存溢出的時(shí)候dump出內(nèi)存堆轉(zhuǎn)儲(chǔ)快照。
/** * VM Args: -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError * @author wangzenghuang */ public class HeapOOMDemo { public static void main(String[] args) { ListstringList = new ArrayList<>(); while(true){ stringList.add("str"); } } }
運(yùn)行結(jié)果,發(fā)生OOM,并且在我們項(xiàng)目的根目錄dump出當(dāng)前的內(nèi)存堆快照
java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid1376.hprof ... Heap dump file created [7972183 bytes in 0.047 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3210) at java.util.Arrays.copyOf(Arrays.java:3181) at java.util.ArrayList.grow(ArrayList.java:261) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227) at java.util.ArrayList.add(ArrayList.java:458) at HeapOOMDemo.main(HeapOOMDemo.java:12) Process finished with exit code 1
簡(jiǎn)單解決思路
那么發(fā)生這個(gè)問(wèn)題以后我們的解決思路有哪些呢?我們可以利用一些工具(例如Eclipse Memory Analyzer
)來(lái)分析dump出的文件,一般來(lái)說(shuō),當(dāng)生產(chǎn)環(huán)境發(fā)生OOM,比較常見(jiàn)的一個(gè)原因是發(fā)生了內(nèi)存泄漏,用工具可以分析出泄露的對(duì)象到GC Root的引用鏈,從而定位到問(wèn)題代碼。假如經(jīng)過(guò)分析后發(fā)現(xiàn)內(nèi)存中的對(duì)象都是“必須存活”的對(duì)象,這時(shí)候就要思考下項(xiàng)目中是否把“-Xms跟-Xmx”設(shè)置得太小了(當(dāng)然這里也不是隨意調(diào)大,需要結(jié)合機(jī)器的物理內(nèi)存情況),再者需要留意代碼中是否有一些長(zhǎng)生命周期的對(duì)象,從代碼中優(yōu)化內(nèi)存消耗。
在jvm的方法區(qū)中,它主要存放了類(lèi)的信息,常量,靜態(tài)變量等。在jdk8以前是通過(guò)“-XX:PermSize,-XX:MaxPermSize”來(lái)調(diào)整這個(gè)區(qū)域的值,但是從8開(kāi)始呢,永久代的概念被MetaSpace(元空間)代替了,對(duì)應(yīng)的參數(shù)也變成了“-XX:MetaspaceSize,-XX:MaxMetaspaceSize”。在這個(gè)例子中使用CGLib來(lái)動(dòng)態(tài)生成一些類(lèi),方便我們實(shí)驗(yàn)操作。
/** * VM Args: -XX:MetaspaceSize=5m -XX:MaxMetaspaceSize=5m * @author wangzenghuang */ public class MethodAreaOOMDemo { public static void main(String[] args) { while(true){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(obj,objects); } }); enhancer.create(); } } static class OOMObject{} }
運(yùn)行結(jié)果
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
簡(jiǎn)單解決方法
這個(gè)問(wèn)題的話,一般來(lái)說(shuō)根據(jù)情況調(diào)整方法區(qū)的大小就行了,網(wǎng)上也有人說(shuō)可以去掉MetaSpace的的大小限制,但是不建議這么干,畢竟不可控的事情我們要少點(diǎn)干,很容易給自己埋雷。
對(duì)于我們來(lái)說(shuō),還有一個(gè)熟悉的錯(cuò)誤,那就是“StackOverflowError”,它是由線程請(qǐng)求的棧深度超過(guò)了jvm允許的最大范圍而產(chǎn)生的。“-Xss”參數(shù)可以設(shè)置棧容量。
/** * VM Args: -Xss128k * @author wangzenghuang */ public class StackOFDemo { private static int stackLength = 1; public void stackLeak(){ stackLength++; stackLeak(); } public static void main(String[] args) { StackOFDemo stackOFDemo = new StackOFDemo(); try { stackOFDemo.stackLeak(); }catch (Throwable e){ System.out.println("length : "+ stackLength); throw e; } } }
運(yùn)行結(jié)果
length : 983 Exception in thread "main" java.lang.StackOverflowError at StackOFDemo.stackLeak(StackOFDemo.java:10) at StackOFDemo.stackLeak(StackOFDemo.java:10) ...
簡(jiǎn)單解決思路
一般來(lái)說(shuō)此類(lèi)問(wèn)題多出現(xiàn)在存在遞歸的地方,要從代碼里重新審視遞歸未結(jié)束的原因,若遞歸的方法沒(méi)問(wèn)題可以根據(jù)實(shí)際情況調(diào)整“-Xss”參數(shù)的大小。還有一些代碼的循壞依賴(lài)也會(huì)造成此類(lèi)情況,
本機(jī)直接內(nèi)存默認(rèn)與“-Xmx”設(shè)定的值一樣大,可以通過(guò)“-XX:MaxDirectMemorySize”修改。
/** * VM Args: -Xmx20m -XX:MaxDirectMemorySize=10 * @author wangzenghuang */ public class DirectMemoryOOMDemo { private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws IllegalAccessException { Field field = Unsafe.class.getDeclaredFields()[0]; field.setAccessible(true); Unsafe unsafe = (Unsafe) field.get(null); while (true){ unsafe.allocateMemory(_1MB); } } }
運(yùn)行結(jié)果
呃,一運(yùn)行這段代碼idea直接閃退了,查閱其他資料可以得知當(dāng)DirectMemory導(dǎo)致內(nèi)存溢出時(shí),Heap Dump文件是很小的,如果程序中有使用NIO的情況可以檢查一下。
這里所展示的代碼只是可以觸發(fā)jvm的各種錯(cuò)誤,但是并不代表這是唯一的觸發(fā)錯(cuò)誤的方方式,假如我們的代碼比較復(fù)雜,有時(shí)候遇到類(lèi)似錯(cuò)誤的時(shí)候還是需要耐心分析。
文章內(nèi)容首發(fā)于微信公眾號(hào)《深夜里的程序猿》,轉(zhuǎn)載請(qǐng)注明出處,侵權(quán)必究。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77495.html
摘要:但是往往越簡(jiǎn)單的東西越容易讓我們忽視,從而導(dǎo)致一些不該有的發(fā)生,作為一名嚴(yán)謹(jǐn)?shù)某绦騿T,怎么能讓這種事情發(fā)生呢所以下面我們就來(lái)了解一下關(guān)于日志的那些正確使用姿勢(shì)。級(jí)別表示出現(xiàn)了嚴(yán)重錯(cuò)誤,程序?qū)?huì)中斷執(zhí)行。 前言 關(guān)于日志,在大家的印象中都是比較簡(jiǎn)單的,只須引入了相關(guān)依賴(lài)包,剩下的事情就是在項(xiàng)目中盡情的打印我們需要的信息了。但是往往越簡(jiǎn)單的東西越容易讓我們忽視,從而導(dǎo)致一些不該有的bug發(fā)...
摘要:前言老王為何半夜慘叫幾行代碼為何導(dǎo)致服務(wù)器爆炸說(shuō)好的線程安全為何還是出問(wèn)題讓我們一起收看今天的走進(jìn)正文出現(xiàn)背景說(shuō)到的出現(xiàn)背景,還得從說(shuō)起。在跟中,都只是調(diào)用的方法,各自都是原子操作,是線程安全的。 前言 老王為何半夜慘叫?幾行代碼為何導(dǎo)致服務(wù)器爆炸?說(shuō)好的線程安全為何還是出問(wèn)題?讓我們一起收看今天的《走進(jìn)IT》 正文 CurrentHashMap出現(xiàn)背景 說(shuō)到ConcurrentHas...
以下是Java技術(shù)棧微信公眾號(hào)發(fā)布的關(guān)于 Java 的技術(shù)干貨,從以下幾個(gè)方面匯總。 Java 基礎(chǔ)篇 Java 集合篇 Java 多線程篇 Java JVM篇 Java 進(jìn)階篇 Java 新特性篇 Java 工具篇 Java 書(shū)籍篇 Java基礎(chǔ)篇 8張圖帶你輕松溫習(xí) Java 知識(shí) Java父類(lèi)強(qiáng)制轉(zhuǎn)換子類(lèi)原則 一張圖搞清楚 Java 異常機(jī)制 通用唯一標(biāo)識(shí)碼UUID的介紹及使用 字符串...
摘要:子組件向父組件通信方法一使用事件父組件向子組件傳遞事件方法,子組件通過(guò)觸發(fā)事件,回調(diào)給父組件。非父子組件兄弟組件之間的數(shù)據(jù)傳遞非父子組件通信,官方推薦使用一個(gè)實(shí)例作為中央事件總線。 寫(xiě)在前面 因?yàn)閷?duì)Vue.js很感興趣,而且平時(shí)工作的技術(shù)棧也是Vue.js,這幾個(gè)月花了些時(shí)間研究學(xué)習(xí)了一下Vue.js源碼,并做了總結(jié)與輸出。 文章的原地址:https://github.com/answ...
摘要:臆想的針對(duì)讀取到的內(nèi)容進(jìn)行操作,比如打印文件內(nèi)容臆想中,讀取文件是有返回值的,將返回值,即文件內(nèi)容,賦給一個(gè)變量,然后決定對(duì)讀取到的內(nèi)容進(jìn)行相應(yīng)的操作,例如打印文件中的內(nèi)容。 臆想的 let fs = require(fs) function readFile(filename){ ... } let content = readFile(config.js) // 針對(duì)讀...
閱讀 1200·2021-09-10 10:51
閱讀 939·2019-08-30 15:53
閱讀 2754·2019-08-30 12:50
閱讀 1004·2019-08-30 11:07
閱讀 2020·2019-08-30 10:50
閱讀 3636·2019-08-29 18:47
閱讀 1339·2019-08-29 18:44
閱讀 1630·2019-08-29 17:01