日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
詳解Java內(nèi)存溢出

內(nèi)存溢出是指應(yīng)用系統(tǒng)中存在無法回收的內(nèi)存或使用的內(nèi)存過多,最終使得程序運(yùn)行要用到的內(nèi)存大于虛擬機(jī)能提供的最大內(nèi)存。

成都創(chuàng)新互聯(lián)客戶idc服務(wù)中心,提供成都二樞服務(wù)器租用托管、成都服務(wù)器、成都主機(jī)托管、成都雙線服務(wù)器等業(yè)務(wù)的一站式服務(wù)。通過各地的服務(wù)中心,我們向成都用戶提供優(yōu)質(zhì)廉價的產(chǎn)品以及開放、透明、穩(wěn)定、高性價比的服務(wù),資深網(wǎng)絡(luò)工程師在機(jī)房提供7*24小時標(biāo)準(zhǔn)級技術(shù)保障。

一、內(nèi)存溢出原因

內(nèi)存溢出就是內(nèi)存不夠,引起內(nèi)存溢出的原因有很多種,常見的有以下幾種:

1、內(nèi)存中加載的數(shù)據(jù)量過于龐大,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù);

2、集合類中有對對象的引用,使用完后未清空,使得JVM不能回收;

3、代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對象實(shí)體;

4、使用的第三方軟件中的BUG;

5、啟動參數(shù)內(nèi)存值設(shè)定的過小;

當(dāng)然實(shí)際情況中內(nèi)存溢出的原因就太多了。下面我們就對這些原因分類一下:

以上的圖是基于java7來敘述的,從上面這張圖我們能夠得到如下信息:java虛擬機(jī)把內(nèi)存分為5個模塊。

(1)程序計(jì)數(shù)器:

程序計(jì)數(shù)器是線程私有的,主要的作用是通過改變這個計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令。既然每個線程都有一個,那么這些線程的計(jì)數(shù)器是互不影響的。也不會拋出任何異常。

(2)虛擬機(jī)棧和本地方法棧:

虛擬機(jī)棧描述的是java方法執(zhí)行的內(nèi)存模型,每個方法在執(zhí)行的時候都會創(chuàng)建一個棧幀用于存儲局部變量表、操作數(shù)棧、動態(tài)連接、方法出口等信息。本地方法棧與虛擬機(jī)棧的區(qū)別是,虛擬機(jī)棧為虛擬機(jī)執(zhí)行java方法服務(wù),而本地方法棧則為虛擬機(jī)提供native方法服務(wù)。

在單線程的操作中,無論是由于棧幀太大,還是虛擬機(jī)??臻g太小,當(dāng)??臻g無法分配時,虛擬機(jī)拋出的都是StackOverflowError異常,而不會得到OutOfMemoryError異常。而在多線程環(huán)境下,則會拋出OutOfMemoryError異常。

(3)java堆和方法區(qū):

java堆區(qū)主要存放對象實(shí)例和數(shù)組等,方法區(qū)保存類信息、常量、靜態(tài)變量等等。運(yùn)行時常量池也是方法區(qū)的一部分。這兩塊區(qū)域是線程共享的區(qū)域,只會拋出OutOfMemoryError。

不知道各位在B站看見過那個面試經(jīng)典場景沒,在回答java的內(nèi)存運(yùn)行數(shù)據(jù)區(qū)結(jié)構(gòu)時,以上的功能作用是一方面,如果回答時把內(nèi)存溢出問題添加上是一個極大的加分項(xiàng)。

二、內(nèi)存溢出實(shí)例

1、堆溢出

既然堆是存放實(shí)例對象的,那我們就無線創(chuàng)建實(shí)例對象。這樣堆區(qū)遲早會滿。

public class HeapOOM { 
    static class User {} 
 public static void main(String[] args) { 
   List list = new ArrayList(); 
         while (true) { 
             list.add(new User()); 
      } 
 } 
} 
/*Exception in thread "main" java.lang.OutOfMemoryError:  
GC overhead limit exceeded 
 at com.fdd.test.HeapOOM.main(HeapOOM.java:11)*/

因?yàn)槲姨崆霸O(shè)置了堆區(qū)內(nèi)存,所以無限創(chuàng)建就會拋出異常。

2、虛擬機(jī)棧和本地方法棧溢出

Java虛擬機(jī)規(guī)范中描述了兩種異常:

如果線程請求的棧深度大于虛擬機(jī)鎖允許的最大深度,將拋出StackOverflowError異常。

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

第一種我們只需要使用方法遞歸調(diào)用即可模擬:

public class StackOutOfMemoryError { 
    public static void main(String[] args) {      
         test(); 
    } 
    private static void go() { 
        System.out.println("StackOverflowError異常"); 
        test(); 
    } 
} 
/*Exception in thread "main" java.lang.StackOverflowError 
 at sun.nio.cs.ext.DoubleByte$Encoder.encodeLoop(DoubleByte.java:617) 
 at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579) 
 at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271) 
 at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125) 
 at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207) 
 at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129) 
 at java.io.PrintStream.write(PrintStream.java:526) 
 at java.io.PrintStream.print(PrintStream.java:597) 
 at java.io.PrintStream.println(PrintStream.java:736) 
 at com.fdd.test.StackOutOfMemoryError.go(StackOutOfMemoryError.java:11) 
 at com.fdd.test.StackOutOfMemoryError.go(StackOutOfMemoryError.java:13)*/

第二種也可以遞歸調(diào)用模擬,,但是使用的是類直接調(diào)用。

public class JavaVMStackSOF { 
    private int stackLength = 1; 
    public void stackLeak() { 
        stackLength++; 
        stackLeak(); 
    } 
 public static void main(String[] args) { 
        JavaVMStackSOF oom = new JavaVMStackSOF(); 
        oom.stackLeak(); 
    } 
} 
/*Exception in thread "main" java.lang.StackOverflowError 
   at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:18) 
   at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) 
   at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) 
   at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) 
   at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) 
   at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) 
   at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) 
   at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) 
   at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) 
   ... */

3、方法區(qū)和運(yùn)行時常量池溢出

public class JavaMethodAreaOOM { 
    public static void main(String[] args) { 
        while (true) { 
            Enhancer enhancer = new Enhancer(); 
            enhancer.setSuperclass(User.class); 
            enhancer.setUseCache(false); 
            enhancer.setCallback(new MethodInterceptor() { 
                public Object intercept(Object obj, Method method, 
                      Object[] args, MethodProxy proxy) throws Throwable { 
                    return proxy.invokeSuper(obj, args); 
                } 
            }); 
            enhancer.create(); 
        } 
    } 
    static class User {} 
} 
/*Exception in thread "main" 
 Exception: java.lang.OutOfMemoryError thrown  
 from the UncaughtExceptionHandler in thread "main" 
*/

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

DirectMemory容量可通過-XX: MaxDirectMemorySize指定,如果不指定,則默認(rèn)與Java堆最大值 (-Xmx指定)一樣。

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); 
        } 
    } 
}

上面介紹了幾個實(shí)例,那遇到這種問題如何排查呢?

三、內(nèi)存溢出排查

排查其實(shí)最主要的就是檢查代碼,而且內(nèi)存溢出往往都是代碼的問題。當(dāng)然一下幾點(diǎn)都是需要注意的:

(1)內(nèi)存中加載的數(shù)據(jù)量過于龐大,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù);

(2)集合類中有對對象的引用,使用完后未清空,使得JVM不能回收;

(3)代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對象實(shí)體;

(4)使用的第三方軟件中的BUG;

(5)啟動參數(shù)內(nèi)存值設(shè)定的過小;

最后就是解決了。

第一步,修改JVM啟動參數(shù),直接增加內(nèi)存。

第二步,檢查錯誤日志

第三步,對代碼進(jìn)行走查和分析,找出可能發(fā)生內(nèi)存溢出的位置。

一般情況下代碼出錯的概率會比較大一些,當(dāng)然了不同的場景不同錯誤總是復(fù)雜多樣的。


文章標(biāo)題:詳解Java內(nèi)存溢出
分享URL:http://www.dlmjj.cn/article/djjhips.html