現在不論是在面試仍是在咱們的工做中,OOM老是不斷的出如今咱們的視野中,因此咱們有必要去了解一下致使OOM的緣由以及一些基本的調整方法,你們能夠經過下面的事例來了解一下什麼樣的代碼會致使OOM,幫助咱們之後在工做中可以經過異常信息來判斷是JVM裏面哪一個區域出現了問題。java
先介紹一下筆者的相關編碼環境。面試
jdk:java version "1.8.0_121"微信
ide:IntelliJ IDEA 2019.1 (Community Edition)jvm
Java中的堆存儲的都是對象實例,當咱們不斷的建立對象,而GC的時候又不能回收,當存儲的對象大小超過了-Xmx的值,這時候則會出現OutOfMemoryError.[-XX:+HeapDumpOnOutOfMemoryError]參數可讓jvm出現內存溢出的時候dump出內存堆轉儲快照。ide
/** * VM Args: -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError * @author wangzenghuang */ public class HeapOOMDemo { public static void main(String[] args) { List<String> stringList = new ArrayList<>(); while(true){ stringList.add("str"); } } }
運行結果,發生OOM,而且在咱們項目的根目錄dump出當前的內存堆快照工具
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
簡單解決思路優化
那麼發生這個問題之後咱們的解決思路有哪些呢?咱們能夠利用一些工具(例如Eclipse Memory Analyzer
)來分析dump出的文件,通常來講,當生產環境發生OOM,比較常見的一個緣由是發生了內存泄漏,用工具能夠分析出泄露的對象到GC Root的引用鏈,從而定位到問題代碼。假如通過分析後發現內存中的對象都是「必須存活」的對象,這時候就要思考下項目中是否把「-Xms跟-Xmx」設置得過小了(固然這裏也不是隨意調大,須要結合機器的物理內存狀況),再者須要留意代碼中是否有一些長生命週期的對象,從代碼中優化內存消耗。編碼
在jvm的方法區中,它主要存放了類的信息,常量,靜態變量等。在jdk8之前是經過「-XX:PermSize,-XX:MaxPermSize」來調整這個區域的值,可是從8開始呢,永久代的概念被MetaSpace(元空間)代替了,對應的參數也變成了「-XX:MetaspaceSize,-XX:MaxMetaspaceSize」。在這個例子中使用CGLib來動態生成一些類,方便咱們實驗操做。idea
/** * 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{} }
運行結果spa
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
簡單解決方法
這個問題的話,通常來講根據狀況調整方法區的大小就好了,網上也有人說能夠去掉MetaSpace的的大小限制,可是不建議這麼幹,畢竟不可控的事情咱們要少點幹,很容易給本身埋雷。
對於咱們來講,還有一個熟悉的錯誤,那就是「StackOverflowError」,它是由線程請求的棧深度超過了jvm容許的最大範圍而產生的。「-Xss」參數能夠設置棧容量。
/** * 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; } } }
運行結果
length : 983 Exception in thread "main" java.lang.StackOverflowError at StackOFDemo.stackLeak(StackOFDemo.java:10) at StackOFDemo.stackLeak(StackOFDemo.java:10) ...
簡單解決思路
通常來講此類問題多出如今存在遞歸的地方,要從代碼裏從新審視遞歸未結束的緣由,若遞歸的方法沒問題能夠根據實際狀況調整「-Xss」參數的大小。還有一些代碼的循壞依賴也會形成此類狀況,
本機直接內存默認與「-Xmx」設定的值同樣大,能夠經過「-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); } } }
運行結果
呃,一運行這段代碼idea直接閃退了,查閱其餘資料能夠得知當DirectMemory致使內存溢出時,Heap Dump文件是很小的,若是程序中有使用NIO的狀況能夠檢查一下。
這裏所展現的代碼只是能夠觸發jvm的各類錯誤,可是並不表明這是惟一的觸發錯誤的方方式,假如咱們的代碼比較複雜,有時候遇到相似錯誤的時候仍是須要耐心分析。
文章內容首發於微信公衆號《深夜裏的程序猿》,轉載請註明出處,侵權必究。