真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何理解JVM內(nèi)存區(qū)域及內(nèi)存溢出分析

本篇文章給大家分享的是有關(guān)如何理解JVM 內(nèi)存區(qū)域及內(nèi)存溢出分析,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

成都創(chuàng)新互聯(lián)是一家專注于網(wǎng)站設(shè)計、網(wǎng)站建設(shè)與策劃設(shè)計,長清網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務涵蓋:長清等地區(qū)。長清做網(wǎng)站價格咨詢:13518219792

前言

在JVM的管控下,Java程序員不再需要管理內(nèi)存的分配與釋放,這和在C和C++的世界是完全不一樣的。所以,在JVM的幫助下,Java程序員很少會關(guān)注內(nèi)存泄露和內(nèi)存溢出的問題。但是,一旦JVM發(fā)生這些情況的時候,如果你不清楚JVM內(nèi)存的內(nèi)存管理機制是很難定位與解決問題的。

一、JVM 內(nèi)存區(qū)域

Java虛擬機在運行時,會把內(nèi)存空間分為若干個區(qū)域,根據(jù)《Java虛擬機規(guī)范(Java SE 7 版)》的規(guī)定,Java虛擬機所管理的內(nèi)存區(qū)域分為如下部分:方法區(qū)、堆內(nèi)存、虛擬機棧、本地方法棧、程序計數(shù)器。

如何理解JVM 內(nèi)存區(qū)域及內(nèi)存溢出分析

1、方法區(qū)

方法區(qū)主要用于存儲虛擬機加載的類信息、常量、靜態(tài)變量,以及編譯器編譯后的代碼等數(shù)據(jù)。在jdk1.7及其之前,方法區(qū)是堆的一個“邏輯部分”(一片連續(xù)的堆空間),但為了與堆做區(qū)分,方法區(qū)還有個名字叫“非堆”,也有人用“永久代”(HotSpot對方法區(qū)的實現(xiàn)方法)來表示方法區(qū)。

從jdk1.7已經(jīng)開始準備“去永久代”的規(guī)劃,jdk1.7的HotSpot中,已經(jīng)把原本放在方法區(qū)中的靜態(tài)變量、字符串常量池等移到堆內(nèi)存中,(常量池除字符串常量池還有class常量池等),這里只是把字符串常量池移到堆內(nèi)存中;在jdk1.8中,方法區(qū)已經(jīng)不存在,原方法區(qū)中存儲的類信息、編譯后的代碼數(shù)據(jù)等已經(jīng)移動到了元空間(MetaSpace)中,元空間并沒有處于堆內(nèi)存上,而是直接占用的本地內(nèi)存(NativeMemory)。根據(jù)網(wǎng)上的資料結(jié)合自己的理解對jdk1.3~1.6、jdk1.7、jdk1.8中方法區(qū)的變遷畫了張圖如下(如有不合理的地方希望讀者指出):

如何理解JVM 內(nèi)存區(qū)域及內(nèi)存溢出分析

去永久代的原因有:

(1)字符串存在永久代中,容易出現(xiàn)性能問題和內(nèi)存溢出。

(2)類及方法的信息等比較難確定其大小,因此對于永久代的大小指定比較困難,太小容易出現(xiàn)永久代溢出,太大則容易導致老年代溢出。

(3)永久代會為 GC 帶來不必要的復雜度,并且回收效率偏低。

2、堆內(nèi)存

堆內(nèi)存主要用于存放對象和數(shù)組,它是JVM管理的內(nèi)存中最大的一塊區(qū)域,堆內(nèi)存和方法區(qū)都被所有線程共享,在虛擬機啟動時創(chuàng)建。在垃圾收集的層面上來看,由于現(xiàn)在收集器基本上都采用分代收集算法,因此堆還可以分為新生代(YoungGeneration)和老年代(OldGeneration),新生代還可以分為 Eden、From Survivor、To Survivor。

3、程序計數(shù)器

程序計數(shù)器是一塊非常小的內(nèi)存空間,可以看做是當前線程執(zhí)行字節(jié)碼的行號指示器,每個線程都有一個獨立的程序計數(shù)器,因此程序計數(shù)器是線程私有的一塊空間,此外,程序計數(shù)器是Java虛擬機規(guī)定的唯一不會發(fā)生內(nèi)存溢出的區(qū)域。

4、虛擬機棧

虛擬機棧也是每個線程私有的一塊內(nèi)存空間,它描述的是方法的內(nèi)存模型,直接看下圖所示:

如何理解JVM 內(nèi)存區(qū)域及內(nèi)存溢出分析

虛擬機會為每個線程分配一個虛擬機棧,每個虛擬機棧中都有若干個棧幀,每個棧幀中存儲了局部變量表、操作數(shù)棧、動態(tài)鏈接、返回地址等。一個棧幀就對應 Java 代碼中的一個方法,當線程執(zhí)行到一個方法時,就代表這個方法對應的棧幀已經(jīng)進入虛擬機棧并且處于棧頂?shù)奈恢?,每一個 Java 方法從被調(diào)用到執(zhí)行結(jié)束,就對應了一個棧幀從入棧到出棧的過程。

5、本地方法棧

本地方法棧與虛擬機棧的區(qū)別是,虛擬機棧執(zhí)行的是 Java 方法,本地方法棧執(zhí)行的是本地方法(Native Method),其他基本上一致,在 HotSpot 中直接把本地方法棧和虛擬機棧合二為一,這里暫時不做過多敘述。

6、元空間

上面說到,jdk1.8 中,已經(jīng)不存在永久代(方法區(qū)),替代它的一塊空間叫做 “ 元空間 ”,和永久代類似,都是 JVM 規(guī)范對方法區(qū)的實現(xiàn),但是元空間并不在虛擬機中,而是使用本地內(nèi)存,元空間的大小僅受本地內(nèi)存限制,但可以通過 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 來指定元空間的大小。

二、JVM 內(nèi)存溢出

1、堆內(nèi)存溢出

堆內(nèi)存中主要存放對象、數(shù)組等,只要不斷地創(chuàng)建這些對象,并且保證 GC Roots 到對象之間有可達路徑來避免垃圾收集回收機制清除這些對象,當這些對象所占空間超過最大堆容量時,就會產(chǎn)生 OutOfMemoryError 的異常。堆內(nèi)存異常示例如下:

/**
* 設(shè)置最大堆最小堆:-Xms20m -Xmx20m
* 運行時,不斷在堆中創(chuàng)建OOMObject類的實例對象,且while執(zhí)行結(jié)束之前,GC Roots(代碼中的oomObjectList)到對象(每一個OOMObject對象)之間有可達路徑,垃圾收集器就無法回收它們,最終導致內(nèi)存溢出。
*/
public class HeapOOM {
    static class OOMObject {
    }
    public static void main(String[] args) {
        List oomObjectList = new ArrayList<>();
        while (true) {
            oomObjectList.add(new OOMObject());
        }
    }
}

運行后會報異常,在堆棧信息中可以看到:

java.lang.OutOfMemoryError: Java heap space 的信息,說明在堆內(nèi)存空間產(chǎn)生內(nèi)存溢出的異常。

新產(chǎn)生的對象最初分配在新生代,新生代滿后會進行一次 Minor GC,如果 Minor GC 后空間不足會把該對象和新生代滿足條件的對象放入老年代,老年代空間不足時會進行 Full GC,之后如果空間還不足以存放新對象則拋出 OutOfMemoryError 異常。

常見原因:內(nèi)存中加載的數(shù)據(jù)過多如一次從數(shù)據(jù)庫中取出過多數(shù)據(jù);集合對對象引用過多且使用完后沒有清空;代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復對象;堆內(nèi)存分配不合理;網(wǎng)絡(luò)連接問題、數(shù)據(jù)庫問題等。

2、虛擬機棧/本地方法棧溢出

(1)StackOverflowError:當線程請求的棧的深度大于虛擬機所允許的最大深度,則拋出StackOverflowError,簡單理解就是虛擬機棧中的棧幀數(shù)量過多(一個線程嵌套調(diào)用的方法數(shù)量過多)時,就會拋出StackOverflowError異常。

最常見的場景就是方法無限遞歸調(diào)用,如下:

/**
* 設(shè)置每個線程的棧大?。?Xss256k
* 運行時,不斷調(diào)用doSomething()方法,main線程不斷創(chuàng)建棧幀并入棧,導致棧的深度越來越大,最終導致棧溢出。
*/
public class StackSOF {
    private int stackLength=1;
    public void doSomething(){
            stackLength++;
            doSomething();
    }
    public static void main(String[] args) {
        StackSOF stackSOF=new StackSOF();
        try {
            stackSOF.doSomething();
        }catch (Throwable e){//注意捕獲的是Throwable
            System.out.println("棧深度:"+stackSOF.stackLength);
            throw e;
        }
    }
}

上述代碼執(zhí)行后拋出:

Exception in thread "Thread-0" java.lang.StackOverflowError 的異常。

(2)OutOfMemoryError:如果虛擬機在擴展棧時無法申請到足夠的內(nèi)存空間,則拋出 OutOfMemoryError。

我們可以這樣理解,虛擬機中可以供棧占用的空間≈可用物理內(nèi)存 - 最大堆內(nèi)存 - 最大方法區(qū)內(nèi)存,比如一臺機器內(nèi)存為 4G,系統(tǒng)和其他應用占用 2G,虛擬機可用的物理內(nèi)存為 2G,最大堆內(nèi)存為 1G,最大方法區(qū)內(nèi)存為 512M,那可供棧占有的內(nèi)存大約就是 512M,假如我們設(shè)置每個線程棧的大小為 1M,那虛擬機中最多可以創(chuàng)建 512個線程,超過 512個線程再創(chuàng)建就沒有空間可以給棧了,就報 OutOfMemoryError 異常了。

如何理解JVM 內(nèi)存區(qū)域及內(nèi)存溢出分析

棧上能夠產(chǎn)生 OutOfMemoryError 的示例如下:

/**
* 設(shè)置每個線程的棧大?。?Xss2m
* 運行時,不斷創(chuàng)建新的線程(且每個線程持續(xù)執(zhí)行),每個線程對一個一個棧,最終沒有多余的空間來為新的線程分配,導致OutOfMemoryError
*/
public class StackOOM {
    private static int threadNum = 0;
    public void doSomething() {
        try {
            Thread.sleep(100000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        final StackOOM stackOOM = new StackOOM();
        try {
            while (true) {
                threadNum++;
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        stackOOM.doSomething();
                    }
                });
                thread.start();
            }
        } catch (Throwable e) {
            System.out.println("目前活動線程數(shù)量:" + threadNum);
            throw e;
        }
    }
}

上述代碼運行后會報異常

在堆棧信息中可以看到java.lang.OutOfMemoryError: unable to create new native thread的信息,無法創(chuàng)建新的線程,說明是在擴展棧的時候產(chǎn)生的內(nèi)存溢出異常。

總結(jié):在線程較少的時候,某個線程請求深度過大,會報 StackOverflow 異常,解決這種問題可以適當加大棧的深度(增加??臻g大?。?,也就是把 -Xss 的值設(shè)置大一些,但一般情況下是代碼問題的可能性較大;在虛擬機產(chǎn)生線程時,無法為該線程申請??臻g了。

會報 OutOfMemoryError 異常,解決這種問題可以適當減小棧的深度,也就是把 -Xss 的值設(shè)置小一些,每個線程占用的空間小了,總空間一定就能容納更多的線程,但是操作系統(tǒng)對一個進程的線程數(shù)有限制,經(jīng)驗值在 3000~5000 左右。

在 jdk1.5 之前 -Xss 默認是 256k,jdk1.5 之后默認是 1M,這個選項對系統(tǒng)硬性還是蠻大的,設(shè)置時要根據(jù)實際情況,謹慎操作。

3、方法區(qū)溢出

前面說到,方法區(qū)主要用于存儲虛擬機加載的類信息、常量、靜態(tài)變量,以及編譯器編譯后的代碼等數(shù)據(jù),所以方法區(qū)溢出的原因就是沒有足夠的內(nèi)存來存放這些數(shù)據(jù)。

由于在 jdk1.6 之前字符串常量池是存在于方法區(qū)中的,所以基于 jdk1.6 之前的虛擬機,可以通過不斷產(chǎn)生不一致的字符串(同時要保證和 GC Roots 之間保證有可達路徑)來模擬方法區(qū)的 OutOfMemoryError 異常;但方法區(qū)還存儲加載的類信息,所以基于 jdk1.7 的虛擬機,可以通過動態(tài)不斷創(chuàng)建大量的類來模擬方法區(qū)溢出。

/**
* 設(shè)置方法區(qū)最大、最小空間:-XX:PermSize=10m -XX:MaxPermSize=10m
* 運行時,通過cglib不斷創(chuàng)建JavaMethodAreaOOM的子類,方法區(qū)中類信息越來越多,最終沒有可以為新的類分配的內(nèi)存導致內(nèi)存溢出
*/
public class JavaMethodAreaOOM {
    public static void main(final String[] args){
       try {
           while (true){
               Enhancer enhancer=new Enhancer();
               enhancer.setSuperclass(JavaMethodAreaOOM.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,objects);
                   }
               });
               enhancer.create();
           }
       }catch (Throwable t){
           t.printStackTrace();
       }
    }
}

上述代碼運行后會報:

 java.lang.OutOfMemoryError: PermGen space 的異常,說明是在方法區(qū)出現(xiàn)了內(nèi)存溢出的錯誤。

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

本機直接內(nèi)存(DirectMemory)并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是 Java 虛擬機規(guī)范中定義的內(nèi)存區(qū)域,但 Java 中用到 NIO 相關(guān)操作時(比如 ByteBuffer 的 allocteDirect 方法申請的是本機直接內(nèi)存),也可能會出現(xiàn)內(nèi)存溢出的異常。

JVM內(nèi)存區(qū)域劃分,便于它能夠更加高效的管理自身的內(nèi)存。當程序中出現(xiàn)這種由于JVM造成的內(nèi)存溢出的情況的時候,需要根據(jù)不同的情況做不同的分析與處理。

以上就是如何理解JVM 內(nèi)存區(qū)域及內(nèi)存溢出分析,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學到更多知識。更多詳情敬請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


當前標題:如何理解JVM內(nèi)存區(qū)域及內(nèi)存溢出分析
文章起源:http://weahome.cn/article/jppgih.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部