論JVM爆炸的幾種姿勢及自救方法

前言

如今不管是在面試還是在我們的工作中,OOM總是不斷的出現(xiàn)在我們的視野中,所以我們有必要去了解一下導(dǎo)致OOM的原因以及一些基本的調(diào)整方法,大家可以通過下面的事例來了解一下什么樣的代碼會導(dǎo)致OOM,幫助我們以后在工作中能夠通過異常信息來判斷是JVM里面哪個(gè)區(qū)域出現(xiàn)了問題。

成都創(chuàng)新互聯(lián)公司專注于通海企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站開發(fā),商城開發(fā)。通海網(wǎng)站建設(shè)公司,為通海等地區(qū)提供建站服務(wù)。全流程定制網(wǎng)站開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)

先介紹一下筆者的相關(guān)編碼環(huán)境。

jdk:java version "1.8.0_121"

ide:IntelliJ IDEA 2019.1 (Community Edition)


正文
1.Java堆溢出

Java中的堆存儲的都是對象實(shí)例,當(dāng)我們不斷的創(chuàng)建對象,而GC的時(shí)候又不能回收,當(dāng)存儲的對象大小超過了-Xmx的值,這時(shí)候則會出現(xiàn)OutOfMemoryError.[-XX:+HeapDumpOnOutOfMemoryError]參數(shù)可以讓jvm出現(xiàn)內(nèi)存溢出的時(shí)候dump出內(nèi)存堆轉(zhuǎn)儲快照。

/**
 * 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");
        }
    }
}

運(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

簡單解決思路

那么發(fā)生這個(gè)問題以后我們的解決思路有哪些呢?我們可以利用一些工具(例如Eclipse Memory Analyzer
)來分析dump出的文件,一般來說,當(dāng)生產(chǎn)環(huán)境發(fā)生OOM,比較常見的一個(gè)原因是發(fā)生了內(nèi)存泄漏,用工具可以分析出泄露的對象到GC Root的引用鏈,從而定位到問題代碼。假如經(jīng)過分析后發(fā)現(xiàn)內(nèi)存中的對象都是“必須存活”的對象,這時(shí)候就要思考下項(xiàng)目中是否把“-Xms跟-Xmx”設(shè)置得太小了(當(dāng)然這里也不是隨意調(diào)大,需要結(jié)合機(jī)器的物理內(nèi)存情況),再者需要留意代碼中是否有一些長生命周期的對象,從代碼中優(yōu)化內(nèi)存消耗。

2.方法區(qū)溢出

在jvm的方法區(qū)中,它主要存放了類的信息,常量,靜態(tài)變量等。在jdk8以前是通過“-XX:PermSize,-XX:MaxPermSize”來調(diào)整這個(gè)區(qū)域的值,但是從8開始呢,永久代的概念被MetaSpace(元空間)代替了,對應(yīng)的參數(shù)也變成了“-XX:MetaspaceSize,-XX:MaxMetaspaceSize”。在這個(gè)例子中使用CGLib來動態(tà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

簡單解決方法

這個(gè)問題的話,一般來說根據(jù)情況調(diào)整方法區(qū)的大小就行了,網(wǎng)上也有人說可以去掉MetaSpace的的大小限制,但是不建議這么干,畢竟不可控的事情我們要少點(diǎn)干,很容易給自己埋雷。

3.棧溢出

對于我們來說,還有一個(gè)熟悉的錯(cuò)誤,那就是“StackOverflowError”,它是由線程請求的棧深度超過了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)
    ...

簡單解決思路

一般來說此類問題多出現(xiàn)在存在遞歸的地方,要從代碼里重新審視遞歸未結(jié)束的原因,若遞歸的方法沒問題可以根據(jù)實(shí)際情況調(diào)整“-Xss”參數(shù)的大小。還有一些代碼的循壞依賴也會造成此類情況,

4.直接內(nèi)存溢出

本機(jī)直接內(nèi)存默認(rèn)與“-Xmx”設(shè)定的值一樣大,可以通過“-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的情況可以檢查一下。

總結(jié)

這里所展示的代碼只是可以觸發(fā)jvm的各種錯(cuò)誤,但是并不代表這是唯一的觸發(fā)錯(cuò)誤的方方式,假如我們的代碼比較復(fù)雜,有時(shí)候遇到類似錯(cuò)誤的時(shí)候還是需要耐心分析。


公眾號博文同步Github倉庫,有興趣的朋友可以幫忙給個(gè)Star哦,碼字不易,感謝支持。

https://github.com/PeppaLittlePig/blog-wechat

分享標(biāo)題:論JVM爆炸的幾種姿勢及自救方法
轉(zhuǎn)載源于:http://muchs.cn/article48/jojehp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站定制開發(fā)、網(wǎng)站維護(hù)企業(yè)建站、搜索引擎優(yōu)化品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

綿陽服務(wù)器托管