《深入理解JVM虛擬機》讀書筆記-內(nèi)存溢出異常

jvm中唯一一個沒有OOM的地方:

程序計數(shù)器

Java堆溢出

創(chuàng)造heap溢出的條件

  • Java堆用于儲存對象實例

  • 不斷地創(chuàng)建對象姻檀,并且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象

  • 隨著對象數(shù)量的增加命满,總?cè)萘坑|及最大堆的容量限制后就會產(chǎn)生內(nèi)存溢出異常。

Java堆內(nèi)存溢出異常測試


/**

 * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

 * @author zzm

 */

public class HeapOOM {

    static class OOMObject {

    }

    public static void main(String[] args) {

        List<OOMObject> list = new ArrayList<OOMObject>();

        while (true) {

            list.add(new OOMObject());

        }

    }

}

運行結(jié)果:


java.lang.OutOfMemoryError: Java heap space

Dumping heap to java_pid3404.hprof ...

Heap dump file created [22045981 bytes in 0.663 secs]

Java堆內(nèi)存的OutOfMemoryError異常是實際應(yīng)用中最常見的內(nèi)存溢出異常情況绣版。出現(xiàn)Java堆內(nèi)存溢出時胶台,異常堆棧信息“java.lang.OutOfMemoryError”會跟隨進一步提示“Java heap space”。

排查手段

要解決這個內(nèi)存區(qū)域的異常僵娃,常規(guī)的處理方法是首先通過內(nèi)存映像分析工具(如Eclipse Memory Analyzer)對Dump出來的堆轉(zhuǎn)儲快照進行分析概作。

  • 第一步首先應(yīng)確認內(nèi)存中導(dǎo)致OOM的對象是否是必要的腋妙,也就是要先分清楚到底是出現(xiàn)了內(nèi)存泄漏(Memory Leak)還是內(nèi)存溢出(Memory Overflow)默怨。

  • 如果是內(nèi)存泄漏,可進一步通過工具查看泄漏對象到GC Roots的引用鏈骤素,找到泄漏對象是通過怎樣的引用路徑匙睹、與哪些GC Roots相關(guān)聯(lián)济竹,才導(dǎo)致垃圾收集器無法回收它們,根據(jù)泄漏對象的類型信息以及它到GC Roots引用鏈的信息送浊,一般可以比較準(zhǔn)確地定位到這些對象創(chuàng)建的位置,進而找出產(chǎn)生內(nèi)存泄漏的代碼的具體位置唁桩。

  • 如果不是內(nèi)存泄漏耸棒,換句話說就是內(nèi)存中的對象確實都是必須存活的,那就應(yīng)當(dāng)檢查Java虛擬機的堆參數(shù)(-Xmx與-Xms)設(shè)置与殃,與機器的內(nèi)存對比单山,看看是否還有向上調(diào)整的空間碍现。

    • 再從代碼上檢查是否存在某些對象生命周期過長、持有狀態(tài)時間過長米奸、存儲結(jié)構(gòu)設(shè)計不合理等情況昼接,盡量減少程序運行期的內(nèi)存消耗。

虛擬機棧和本地方法棧溢出

由于HotSpot虛擬機中并不區(qū)分虛擬機棧和本地方法棧悴晰,因此對于HotSpot來說辩棒,-Xoss參數(shù)(設(shè)置本地方法棧大小)雖然存在膨疏,但實際上是沒有任何效果的一睁,棧容量只能由-Xss參數(shù)來設(shè)定。


關(guān)于虛擬機棧和本地方法棧佃却,在《Java虛擬機規(guī)范》中描述了兩種異常:

  • 如果<font color=red>線程請求的棧深度大于虛擬機所允許的最大深度</font>者吁,將拋出StackOverflowError異常。

  • 如果虛擬機的<font color=red>棧內(nèi)存允許動態(tài)擴展饲帅,當(dāng)擴展棧容量無法申請到足夠的內(nèi)存時</font>复凳,將拋出OutOfMemoryError異常。


HotSpot虛擬機的異常

《Java虛擬機規(guī)范》明確允許Java虛擬機實現(xiàn)自行選擇是否支持棧的動態(tài)擴展灶泵,而HotSpot虛擬機的選擇是不支持擴展

  • 所以除非在創(chuàng)建線程申請內(nèi)存時就因無法獲得足夠內(nèi)存而出現(xiàn)OutOfMemoryError異常育八,否則在線程運行時是<font color=red>不會因為擴展而導(dǎo)致內(nèi)存溢出的</font>。

  • 只會因為棧容量無法容納新的棧幀而導(dǎo)致StackOverflowError異常赦邻。

驗證

用例1

使用-Xss參數(shù)減少棧內(nèi)存容量髓棋。

結(jié)果:拋出StackOverflowError異常,異常出現(xiàn)時輸出的堆棧深度相應(yīng)縮小惶洲。


/**

 * VM Args:-Xss128k

 * @author zzm

 */

public class JavaVMStackSOF {

    private int stackLength = 1;

    public void stackLeak() {

        stackLength++;

        stackLeak();

    }

     public static void main(String[] args) throws Throwable {

        JavaVMStackSOF oom = new JavaVMStackSOF();

        try {

            oom.stackLeak();

        } catch (Throwable e) {

            System.out.println("stack length:" + oom.stackLength);

            throw e;

        }

    }

}

運行結(jié)果:


stack length:2402

Exception in thread "main" java.lang.StackOverflowError

    at org.fenixsoft.oom. JavaVMStackSOF.leak(JavaVMStackSOF.java:20)

    at org.fenixsoft.oom. JavaVMStackSOF.leak(JavaVMStackSOF.java:21)

    at org.fenixsoft.oom. JavaVMStackSOF.leak(JavaVMStackSOF.java:21)

……后續(xù)異常堆棧信息省略

? 對于不同版本的Java虛擬機和不同的操作系統(tǒng),棧容量最小值可能會有所限制签则,這主要取決于操作系統(tǒng)內(nèi)存分頁大小铐料。譬如上述方法中的參數(shù)-Xss128k可以正常用于32位Windows系統(tǒng)下的JDK 6柒凉,但是如果用于64位Windows系統(tǒng)下的JDK 11扛拨,則會提示棧容量最小不能低于180K绑警,而在Linux下這個值則可能是228K渴频,如果低于這個最小限制卜朗,HotSpot虛擬器啟動時會給出如下提示:


The Java thread stack size specified is too small. Specify at least 228k

用例2

定義了大量的本地變量场钉,增大此方法幀中本地變量表的長度。

結(jié)果:拋出StackOverflowError異常批钠,異常出現(xiàn)時輸出的堆棧深度相應(yīng)縮小指郁。

首先闲坎,對第一種情況進行測試箫柳,具體如代碼清單2-4所示库糠。


/**

 * @author zzm

 */

public class JavaVMStackSOF {

    private static int stackLength = 0

    private static int stackLength = 0;

    public static void test() {

        long unused1, unused2, unused3, unused4, unused5,

             unused6, unused7, unused8, unused9, unused10,

             unused11, unused12, unused13, unused14, unused15,

             unused16, unused17, unused18, unused19, unused20,

             unused21, unused22, unused23, unused24, unused25,

             unused26, unused27, unused28, unused29, unused30,

             unused31, unused32, unused33, unused34, unused35,

             unused36, unused37, unused38, unused39, unused40,

             unused41, unused42, unused43, unused44, unused45,

             unused46, unused47, unused48, unused49, unused50,

             unused51, unused52, unused53, unused54, unused55,

             unused56, unused57, unused58, unused59, unused60,

             unused61, unused62, unused63, unused64, unused65,

             unused66, unused67, unused68, unused69, unused70,

             unused71, unused72, unused73, unused74, unused75,

             unused76, unused77, unused78, unused79, unused80,

             unused81, unused82, unused83, unused84, unused85,

             unused86, unused87, unused88, unused89, unused90,

             unused91, unused92, unused93, unused94, unused95,

             unused96, unused97, unused98, unused99, unused100;

        stackLength ++;

        test();

        unused1 = unused2 = unused3 = unused4 = unused5 =

        unused6 = unused7 = unused8 = unused9 = unused10 =

        unused11 = unused12 = unused13 = unused14 = unused15 =

        unused16 = unused17 = unused18 = unused19 = unused20 =

        unused21 = unused22 = unused23 = unused24 = unused25 =

        unused26 = unused27 = unused28 = unused29 = unused30 =

        unused31 = unused32 = unused33 = unused34 = unused35 =

        unused36 = unused37 = unused38 = unused39 = unused40 =

        unused41 = unused42 = unused43 = unused44 = unused45 =

        unused46 = unused47 = unused48 = unused49 = unused50 =

        unused51 = unused52 = unused53 = unused54 = unused55 =

        unused56 = unused57 = unused58 = unused59 = unused60 =

        unused61 = unused62 = unused63 = unused64 = unused65 =

        unused66 = unused67 = unused68 = unused69 = unused70 =

        unused71 = unused72 = unused73 = unused74 = unused75 =

        unused71 = unused72 = unused73 = unused74 = unused75 =

        unused76 = unused77 = unused78 = unused79 = unused80 =

        unused81 = unused82 = unused83 = unused84 = unused85 =

        unused86 = unused87 = unused88 = unused89 = unused90 =

        unused91 = unused92 = unused93 = unused94 = unused95 =

        unused96 = unused97 = unused98 = unused99 = unused100 = 0;

    }

    public static void main(String[] args) {

        try {

            test();

        }catch (Error e){

            System.out.println("stack length:" + stackLength);

            throw e;

        }

    }

}

運行結(jié)果


stack length:5675

Exception in thread "main" java.lang.StackOverflowError

    at org.fenixsoft.oom. JavaVMStackSOF.leak(JavaVMStackSOF.java:27)

    at org.fenixsoft.oom. JavaVMStackSOF.leak(JavaVMStackSOF.java:28)

    at org.fenixsoft.oom. JavaVMStackSOF.leak(JavaVMStackSOF.java:28)

……后續(xù)異常堆棧信息省略

實驗結(jié)果表明:

<font color=red>無論是由于棧幀太大還是虛擬機棧容量太小艘虎,當(dāng)新的棧幀內(nèi)存無法分配的時候属划,HotSpot虛擬機拋出的都是StackOverflowError異常</font>。

元空間溢出

Java 8及之后的版本使用Metaspace來替代永久代绽昼。

Metaspace是方法區(qū)在Hotspot 中的實現(xiàn),它與持久代最大的區(qū)別在于:Metaspace并不在虛擬機內(nèi)存中而是使用本地內(nèi)存也即在Java8中, classe metadata(the virtual machines internal presentation of Java class)菱农,被存儲在叫做Metaspace native memory大莫。


Metaspace存放了以下信息:

  • 虛擬機加載的類信息

  • 常量池

  • 靜態(tài)變量

  • 即時編譯后的代碼


模擬Metaspace空間溢出

模擬Metaspace空間溢出只厘,我們借助CGLib直接操作字節(jié)碼運行時不斷生成類往元空間灌,類占據(jù)的空間總是會超過Metaspace指定的空間大小的赋元。

測試類


package com.pl.jvm.metaspace;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;

import org.springframework.cglib.proxy.MethodProxy;

/**

 * <p>

 *

 * @Description: TODO

 * </p>

 * @ClassName MetaSpaceDemo

 * @Author pl

 * @Date 2021/7/7

 * @Version V1.0.0

 */

public class OOMEMetaspaceDemo {

    // 靜態(tài)類

    static class OOMObject {}

    /**

     * -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

     *

     * @param args

     */

    public static void main(final String[] args) {

        // 模擬計數(shù)多少次以后發(fā)生異常

        int i =0;

        try {

            while (true) {

                i++;

                // 使用Spring的動態(tài)字節(jié)碼技術(shù)

                Enhancer enhancer = new Enhancer();

                enhancer.setSuperclass(OOMObject.class);

                enhancer.setUseCache(false);

                enhancer.setCallback(new MethodInterceptor() {

                    @Override

                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

                        return methodProxy.invokeSuper(o, args);

                    }

                });

                enhancer.create();

            }

        } catch (Throwable e) {

            System.out.println("第幾次發(fā)生異常::" + i);

            e.printStackTrace();

        } finally {

        }

    }

}

輸出


第542次發(fā)生異常:

java.lang.OutOfMemoryError: Metaspace

    at java.lang.Class.forName0(Native Method)

    at java.lang.Class.forName(Class.java:348)

    at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:563)

    at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363)

    at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:582)

    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:131)

    at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319)

    at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:569)

    at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:384)

    at com.pl.jvm.metaspace.OOMEMetaspaceDemo.main(OOMEMetaspaceDemo.java:45)

HotSpot提供了一些參數(shù)作為元空間的防御措施,主要包括:

  • -XX:MaxMetaspaceSize:設(shè)置元空間最大值护糖,默認是-1,即不限制献酗,或者說只受限于本地內(nèi)存大小罕偎。

  • -XX:MetaspaceSize:指定元空間的初始空間大小甩苛,以字節(jié)為單位,達到該值就會觸發(fā)垃圾收集進行類型卸載捐迫,同時收集器會對該值進行調(diào)整:如果釋放了大量的空間施戴,就適當(dāng)降低該值;如果釋放了很少的空間辆雾,那么在不超過-XX:MaxMetaspaceSize(如果設(shè)置了的話)的情況下藤乙,適當(dāng)提高該值坛梁。

  • -XX:MinMetaspaceFreeRatio:作用是在垃圾收集之后控制最小的元空間剩余容量的百分比划咐,可減少因為元空間不足導(dǎo)致的垃圾收集的頻率褐缠。

  • -XX:Max-MetaspaceFreeRatio队魏,用于控制最大的元空間剩余容量的百分比。

本機直接內(nèi)存溢出

? 直接內(nèi)存(Direct Memory)的容量大小可通過-XX:MaxDirectMemorySize參數(shù)來指定蟹略,如果不去指定,則<font color=red>默認與Java堆最大值(由-Xmx指定)一致</font>意敛。

一般直接內(nèi)存溢出的原因

? 寫NIO程序經(jīng)常使用ByteBuffer來讀取或者寫入數(shù)據(jù),它可以使用Native函數(shù)庫直接分配堆外內(nèi)存钓猬,然后通過一個存儲在Java堆里面的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。這樣能在一些場景中顯著提高性能澳迫,因為避兔了在Java堆和Native堆中來回復(fù)制數(shù)據(jù)橄登。

  • ByteBuffer.allocate(capability) 第一種方式是分配VM堆內(nèi)存拢锹,屬于GC管轄范圍卒稳,由于需要拷貝所以速度相對較慢展哭。

  • ByteBuffer.allocateDirect(capability) 第二種方式是分配OS本地內(nèi)存匪傍,不屬于GC管轄范圍役衡,由于不需要內(nèi)存拷貝所以速度相對較快手蝎。

? 如果不斷分配本地內(nèi)存,堆內(nèi)存很少使用邮辽,那么JVM就不需要執(zhí)行GC吨述,DirectByteBuffer對象們就不會被回收捕儒,這時候堆內(nèi)存充足刘莹,但本地內(nèi)存可能已經(jīng)使用光了,再次嘗試分配本地內(nèi)存就會出現(xiàn)OutOfMemoryError蒲拉,那程序就直接崩潰了雌团。

Direct buffer memory


import java.nio.ByteBuffer;

import java.util.concurrent.TimeUnit;

public class OOMEDirectBufferMemoryDemo {

    /**

     * -Xms5m -Xmx5m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

     *

     * @param args

     * @throws InterruptedException

     */

    public static void main(String[] args) throws InterruptedException {

        System.out.println(String.format("配置的maxDirectMemory: %.2f MB",//

                sun.misc.VM.maxDirectMemory() / 1024.0 / 1024));

        TimeUnit.SECONDS.sleep(3);

        ByteBuffer bb = ByteBuffer.allocateDirect(6 * 1024 * 1024);

    }   

}

輸出


[GC (Allocation Failure) [PSYoungGen: 1024K->504K(1536K)] 1024K->772K(5632K), 0.0014568 secs] [Times: user=0.09 sys=0.00, real=0.00 secs]

配置的maxDirectMemory: 5.00 MB

[GC (System.gc()) [PSYoungGen: 622K->504K(1536K)] 890K->820K(5632K), 0.0009753 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[Full GC (System.gc()) [PSYoungGen: 504K->0K(1536K)] [ParOldGen: 316K->725K(4096K)] 820K->725K(5632K), [Metaspace: 3477K->3477K(1056768K)], 0.0072268 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

Exception in thread "main" Heap

 PSYoungGen      total 1536K, used 40K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)

  eden space 1024K, 4% used [0x00000000ffe00000,0x00000000ffe0a3e0,0x00000000fff00000)

  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)

  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)

 ParOldGen       total 4096K, used 725K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)

  object space 4096K, 17% used [0x00000000ffa00000,0x00000000ffab5660,0x00000000ffe00000)

 Metaspace       used 3508K, capacity 4566K, committed 4864K, reserved 1056768K

  class space    used 391K, capacity 394K, committed 512K, reserved 1048576K

java.lang.OutOfMemoryError: Direct buffer memory

    at java.nio.Bits.reserveMemory(Bits.java:694)

    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)

    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)

    at com.lun.jvm.OOMEDirectBufferMemoryDemo.main(OOMEDirectBufferMemoryDemo.java:20)

生成 ByteBuffer 的時候調(diào)用了 allication 方法灵寺,按下面一步接一步的調(diào)用略板,可以看到 System.gc() :

image.png

image.png

image.png

而上述參數(shù):-XX:+DisableExplicitGC 會使 System.gc() 失效,因此當(dāng)某些 nio 的引用到了 old 區(qū)又沒發(fā)生 full gc 的時候瓤檐,就會出現(xiàn)標(biāo)題的 memory 異常了挠蛉。

解決辦法

1)檢查是否直接或間接使用了 nio 绍移,例如手動調(diào)用生成 buffer 的方法或者使用了 nio 容器如 netty, jetty恩敌, tomcat 等等纠炮;

2)-XX:MaxDirectMemorySize 加大,該參數(shù)默認是 64M 耕肩,可以根據(jù)需求調(diào)大試試;

3)檢查 JVM 參數(shù)里面有無: -XX:+DisableExplicitGC 梳虽,如果有就去掉.

OutOfMemoryError


/**

 * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M

 * @author zzm

 */

public class DirectMemoryOOM {

    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) throws Exception {

        Field unsafeField = Unsafe.class.getDeclaredFields()[0];

        unsafeField.setAccessible(true);

        Unsafe unsafe = (Unsafe) unsafeField.get(null);

        while (true) {

            unsafe.allocateMemory(_1MB);

        }

    }

}

運行結(jié)果


Exception in thread "main" java.lang.OutOfMemoryError

    at sun.misc.Unsafe.allocateMemory(Native Method)

    at org.fenixsoft.oom.DMOOM.main(DMOOM.java:20)

? 例子中的代碼越過了DirectByteBuffer類直接通過反射獲取Unsafe實例進行內(nèi)存分配,(Unsafe類的getUnsafe()方法指定只有引導(dǎo)類加載器才會返回實例禀挫,體現(xiàn)了設(shè)計者希望只有虛擬機標(biāo)準(zhǔn)類庫里面的類才能使用Unsafe的功能,在JDK 10時才將Unsafe的部分功能通過VarHandle開放給外部使用)腻格,因為雖然使用DirectByteBuffer分配內(nèi)存也會拋出內(nèi)存溢出異常菜职,但它拋出異常時并沒有真正向操作系統(tǒng)申請分配內(nèi)存酬核,而是通過計算得知內(nèi)存無法分配就會在代碼里手動拋出溢出異常蜜另,真正申請分配內(nèi)存的方法是Unsafe::allocateMemory()。

? 如果直接內(nèi)存導(dǎo)致的內(nèi)存溢出嫡意,一個明顯的特征是在Heap Dump文件中不會看見有什么明顯的異常情況举瑰,如果發(fā)現(xiàn)內(nèi)存溢出之后產(chǎn)生的Dump文件很小,而程序中又直接或間接使用了DirectMemory(典型的間接使用就是NIO)蔬螟,那就可以考慮重點檢查一下直接內(nèi)存方面的原因了此迅。

補充

StackOverflowError和OutofMemoryError

在《java虛擬機規(guī)范》規(guī)范中,對本地方法和虛擬機棧的使用中規(guī)定了兩個異常:StackOverflowError和OutofMemoryError

StackoverFlowError

  • java.lang.StackOverflowError

OutofMemoryError

  • java.lang.OutOfMemoryError:java heap space
  • java.lang.OutOfMemoryError:GC overhead limit exceeeded
  • java.lang.OutOfMemoryError:Direct buffer memory
  • java.lang.OutOfMemoryError:unable to create new native thread
  • java.lang.OutOfMemoryError:Metaspace

對于上面中沒有提到的異常進行補充

GC overhead limit exceeeded

GC overhead limit exceeded

超出GC開銷限制

GC回收時間過長時會拋出OutOfMemroyError旧巾。過長的定義是鲁猩,超過98%的時間用來做GC并且回收了不到2%的堆內(nèi)存疾棵,連續(xù)多次GC 都只回收了不到2%的極端情況下才會拋出。

假如不拋出GC overhead limit錯誤會發(fā)生什么情況呢恩溅?那就是GC清理的這么點內(nèi)存很快會再次填滿奶稠,迫使cc再次執(zhí)行啦辐。這樣就形成惡性循環(huán),CPU使用率一直是100%,而Gc卻沒有任何成果

image.png

import java.util.ArrayList;

import java.util.List;

public class OOMEGCOverheadLimitExceededDemo {

    /**

     * 

     * -Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m

     * 

     * @param args

     */

    public static void main(String[] args) {

        int i = 0;

        List<String> list = new ArrayList<>();

        try {

            while(true) {

                list.add(String.valueOf(++i).intern());

            }

        } catch (Exception e) {

            System.out.println("***************i:" + i);

            e.printStackTrace();

            throw e;

        }

    }

}

輸出


[GC (Allocation Failure) [PSYoungGen: 2048K->498K(2560K)] 2048K->1658K(9728K), 0.0033090 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

[GC (Allocation Failure) [PSYoungGen: 2323K->489K(2560K)] 3483K->3305K(9728K), 0.0020911 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

[GC (Allocation Failure) [PSYoungGen: 2537K->496K(2560K)] 5353K->4864K(9728K), 0.0025591 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

[GC (Allocation Failure) [PSYoungGen: 2410K->512K(2560K)] 6779K->6872K(9728K), 0.0058689 secs] [Times: user=0.09 sys=0.00, real=0.01 secs] 

[Full GC (Ergonomics) [PSYoungGen: 512K->0K(2560K)] [ParOldGen: 6360K->6694K(7168K)] 6872K->6694K(9728K), [Metaspace: 2651K->2651K(1056768K)], 0.0894928 secs] [Times: user=0.42 sys=0.00, real=0.09 secs] 

[Full GC (Ergonomics) [PSYoungGen: 2048K->1421K(2560K)] [ParOldGen: 6694K->6902K(7168K)] 8742K->8324K(9728K), [Metaspace: 2651K->2651K(1056768K)], 0.0514932 secs] [Times: user=0.34 sys=0.00, real=0.05 secs] 

[Full GC (Ergonomics) [PSYoungGen: 2048K->2047K(2560K)] [ParOldGen: 6902K->6902K(7168K)] 8950K->8950K(9728K), [Metaspace: 2651K->2651K(1056768K)], 0.0381615 secs] [Times: user=0.13 sys=0.00, real=0.04 secs] 

...省略89行...

[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7044K->7044K(7168K)] 9092K->9092K(9728K), [Metaspace: 2651K->2651K(1056768K)], 0.0360935 secs] [Times: user=0.25 sys=0.00, real=0.04 secs] 

[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7046K->7046K(7168K)] 9094K->9094K(9728K), [Metaspace: 2651K->2651K(1056768K)], 0.0360458 secs] [Times: user=0.38 sys=0.00, real=0.04 secs] 

[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7048K->7048K(7168K)] 9096K->9096K(9728K), [Metaspace: 2651K->2651K(1056768K)], 0.0353033 secs] [Times: user=0.11 sys=0.00, real=0.04 secs] 

    進行了幾次gc,  gc回收之前 2047  回收之后還是2047       老年代   7050K->7048K(7168K)              Metaspace: 2670K->2670K(1056768K)

***************i:147041

[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7050K->7048K(7168K)] 9098K->9096K(9728K), [Metaspace: 2670K->2670K(1056768K)], 0.0371397 secs] [Times: user=0.22 sys=0.00, real=0.04 secs] 

java.lang.OutOfMemoryError: GC overhead limit exceeded

[Full GC (Ergonomics)     at java.lang.Integer.toString(Integer.java:401)

[PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7051K->7050K(7168K)] 9099K->9097K(9728K), [Metaspace: 2676K->2676K(1056768K)], 0.0434184 secs] [Times: user=0.38 sys=0.00, real=0.04 secs] 

    at java.lang.String.valueOf(String.java:3099)

    at com.lun.jvm.OOMEGCOverheadLimitExceededDemo.main(OOMEGCOverheadLimitExceededDemo.java:19)

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

[Full GC (Ergonomics) [PSYoungGen: 2047K->0K(2560K)] [ParOldGen: 7054K->513K(7168K)] 9102K->513K(9728K), [Metaspace: 2677K->2677K(1056768K)], 0.0056578 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 

    at java.lang.Integer.toString(Integer.java:401)

    at java.lang.String.valueOf(String.java:3099)

    at com.lun.jvm.OOMEGCOverheadLimitExceededDemo.main(OOMEGCOverheadLimitExceededDemo.java:19)

Heap

 PSYoungGen      total 2560K, used 46K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)

  eden space 2048K, 2% used [0x00000000ffd00000,0x00000000ffd0bb90,0x00000000fff00000)

  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)

  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)

 ParOldGen       total 7168K, used 513K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)

  object space 7168K, 7% used [0x00000000ff600000,0x00000000ff6807f0,0x00000000ffd00000)

 Metaspace       used 2683K, capacity 4486K, committed 4864K, reserved 1056768K

  class space    used 285K, capacity 386K, committed 512K, reserved 1048576K

### unable to create new native thread

不能夠創(chuàng)建更多的新的線程了,也就是說創(chuàng)建線程的上限達到了

高并發(fā)請求服務(wù)器時洲脂,經(jīng)常會出現(xiàn)異常java.lang.OutOfMemoryError:unable to create new native thread棚放,準(zhǔn)確說該native thread異常與對應(yīng)的平臺有關(guān)

導(dǎo)致原因:

  • 應(yīng)用創(chuàng)建了太多線程局骤,一個應(yīng)用進程創(chuàng)建多個線程凯傲,超過系統(tǒng)承載極限
  • 服務(wù)器并不允許你的應(yīng)用程序創(chuàng)建這么多線程,linux系統(tǒng)默認運行單個進程可以創(chuàng)建的線程為1024個,如果應(yīng)用創(chuàng)建超過這個數(shù)量,就會報 java.lang.OutOfMemoryError:unable to create new native thread

解決方法:

  1. 想辦法降低你應(yīng)用程序創(chuàng)建線程的數(shù)量挖诸,分析應(yīng)用是否真的需要創(chuàng)建這么多線程,如果不是殉挽,改代碼將線程數(shù)降到最低
  2. 對于有的應(yīng)用投慈,確實需要創(chuàng)建很多線程锁荔,遠超過linux系統(tǒng)默認1024個線程限制(一般到900多就差不多了掛了),可以通過修改Linux服務(wù)器配置响蕴,擴大linux默認限制

public class OOMEUnableCreateNewThreadDemo {

    public static void main(String[] args) {

        for (int i = 0; ; i++) {

            System.out.println("************** i = " + i);

            new Thread(() -> {

                try {

                    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }, String.valueOf(i)).start();

        }

    }

}

#### unable to create new native thread上限調(diào)整

非root用戶登錄Linux系統(tǒng)(CentOS)測試

服務(wù)器級別調(diào)參調(diào)優(yōu)

查看系統(tǒng)線程限制數(shù)目


ulimit -u

修改系統(tǒng)線程限制數(shù)目


vim /etc/security/limits.d/90-nproc.conf

打開后發(fā)現(xiàn)除了root改艇,其他賬戶都限制在1024個

image.png

假如我們想要張三這個用盧運行绵咱,希望他生成的線程多一些,我們可以如下配置


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闹瞧,一起剝皮案震驚了整個濱河市奥邮,隨后出現(xiàn)的幾起案子蘸朋,更是在濱河造成了極大的恐慌吐根,老刑警劉巖冗疮,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碘梢,死亡現(xiàn)場離奇詭異煞躬,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門恩沛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來在扰,“玉大人,你說我怎么就攤上這事雷客∶⒅椋” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵搅裙,是天一觀的道長皱卓。 經(jīng)常有香客問我,道長呈宇,這世上最難降的妖魔是什么好爬? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮甥啄,結(jié)果婚禮上存炮,老公的妹妹穿的比我還像新娘。我一直安慰自己蜈漓,他們只是感情好穆桂,可當(dāng)我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著融虽,像睡著了一般享完。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上有额,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天般又,我揣著相機與錄音,去河邊找鬼巍佑。 笑死嗅战,一個胖子當(dāng)著我的面吹牛咽笼,可吹牛的內(nèi)容都是我干的骤公。 我是一名探鬼主播坏瞄,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脆栋!你這毒婦竟也來了倦卖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤椿争,失蹤者是張志新(化名)和其女友劉穎怕膛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丘薛,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡嘉竟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舍扰。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡倦蚪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出边苹,到底是詐尸還是另有隱情陵且,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布个束,位于F島的核電站慕购,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏茬底。R本人自食惡果不足惜沪悲,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阱表。 院中可真熱鬧殿如,春花似錦、人聲如沸最爬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爱致。三九已至烤送,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間糠悯,已是汗流浹背帮坚。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留互艾,地道東北人叶沛。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像忘朝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子判帮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容