Java內(nèi)存溢出和內(nèi)存泄露

雖然jvm可以通過GC自動(dòng)回收無(wú)用的內(nèi)存,但是代碼不好的話仍然存在內(nèi)存溢出(OOM)風(fēng)險(xiǎn)。

一、為什么要了解內(nèi)存泄露和內(nèi)存溢出?

1、內(nèi)存泄露一般是代碼設(shè)計(jì)存在缺陷導(dǎo)致的,通過了解內(nèi)存泄露的場(chǎng)景,可以避免不必要的內(nèi)存溢出和提高自己的代碼編寫水平;

2、通過了解內(nèi)存溢出的幾種常見情況,可以在出現(xiàn)內(nèi)存溢出的時(shí)候快速的定位問題的位置,縮短解決故障的時(shí)間。

二、什么是內(nèi)存泄露和內(nèi)存溢出

內(nèi)存泄露:指程序中動(dòng)態(tài)分配內(nèi)存給一些臨時(shí)對(duì)象,但是對(duì)象不會(huì)被GC所回收,它始終占用內(nèi)存。即被分配的對(duì)象可達(dá)但已無(wú)用。

內(nèi)存溢出:指程序運(yùn)行過程中無(wú)法申請(qǐng)到足夠的內(nèi)存而導(dǎo)致的一種錯(cuò)誤。內(nèi)存溢出通常發(fā)生于OLD段或Perm段垃圾回收后,仍然無(wú)內(nèi)存空間容納新的Java對(duì)象的情況。

從定義上可以看出內(nèi)存泄露是內(nèi)存溢出的一種誘因,不是唯一因素。

三、內(nèi)存泄露的幾種場(chǎng)景:

1、長(zhǎng)生命周期的對(duì)象持有短生命周期對(duì)象的引用
這是內(nèi)存泄露最常見的場(chǎng)景,也是代碼設(shè)計(jì)中經(jīng)常出現(xiàn)的問題。
例如:在全局靜態(tài)map中緩存局部變量,且沒有清空操作,隨著時(shí)間的推移,這個(gè)map會(huì)越來越大,造成內(nèi)存泄露。

2、修改HashSet中對(duì)象的參數(shù)值,且參數(shù)是計(jì)算哈希值的字段
當(dāng)一個(gè)對(duì)象被存儲(chǔ)進(jìn)HashSet集合中以后,就不能修改這個(gè)對(duì)象中的那些參與計(jì)算哈希值的字段,否則對(duì)象修改后的哈希值與最初存儲(chǔ)進(jìn)HashSet集合中時(shí)的哈希值就不同了,在這種情況下,即使在contains方法使用該對(duì)象的當(dāng)前引用作為參數(shù)去HashSet集合中檢索對(duì)象,也將返回找不到對(duì)象的結(jié)果,這也會(huì)導(dǎo)致無(wú)法從HashSet集合中刪除當(dāng)前對(duì)象,造成內(nèi)存泄露。

3、機(jī)器的連接數(shù)和關(guān)閉時(shí)間設(shè)置
長(zhǎng)時(shí)間開啟非常耗費(fèi)資源的連接,也會(huì)造成內(nèi)存泄露。

四、內(nèi)存溢出的幾種情況:

1、堆內(nèi)存溢出(outOfMemoryError:java heap space)
在jvm規(guī)范中,堆中的內(nèi)存是用來生成對(duì)象實(shí)例和數(shù)組的。
如果細(xì)分,堆內(nèi)存還可以分為年輕代和年老代,年輕代包括一個(gè)eden區(qū)和兩個(gè)survivor區(qū)。
當(dāng)生成新對(duì)象時(shí),內(nèi)存的申請(qǐng)過程如下:
a、jvm先嘗試在eden區(qū)分配新建對(duì)象所需的內(nèi)存;
b、如果內(nèi)存大小足夠,申請(qǐng)結(jié)束,否則下一步;
c、jvm啟動(dòng)youngGC,試圖將eden區(qū)中不活躍的對(duì)象釋放掉,釋放后若Eden空間仍然不足以放入新對(duì)象,則試圖將部分Eden中活躍對(duì)象放入Survivor區(qū);
d、Survivor區(qū)被用來作為Eden及old的中間交換區(qū)域,當(dāng)OLD區(qū)空間足夠時(shí),Survivor區(qū)的對(duì)象會(huì)被移到Old區(qū),否則會(huì)被保留在Survivor區(qū);
e、 當(dāng)OLD區(qū)空間不夠時(shí),JVM會(huì)在OLD區(qū)進(jìn)行full GC;
f、full GC后,若Survivor及OLD區(qū)仍然無(wú)法存放從Eden復(fù)制過來的部分對(duì)象,導(dǎo)致JVM無(wú)法在Eden區(qū)為新對(duì)象創(chuàng)建內(nèi)存區(qū)域,則出現(xiàn)”out of memory錯(cuò)誤”:outOfMemoryError:java heap space

/** 
 * 堆內(nèi)存溢出 
 * jvm參數(shù):-Xms5m -Xmx5m -Xmn2m -XX:NewSize=1m 
 */  
public class MemoryLeak {  
    private String[] s = new String[1000];  
    public static void main(String[] args) throws InterruptedException{  
        Map<String,Object> m =new HashMap<String,Object>();  
        int i =0;  
        int j=10000;  
        while(true){  
            for(;i<j;i++){  
                MemoryLeak memoryLeak = new MemoryLeak();  
                m.put(String.valueOf(i), memoryLeak);  
            }  
        }  
    }  
}

2、方法區(qū)內(nèi)存溢出(outOfMemoryError:permgem space)
在jvm規(guī)范中,方法區(qū)主要存放的是類信息、常量、靜態(tài)變量等。
所以如果程序加載的類過多,或者使用反射、gclib等這種動(dòng)態(tài)代理生成類的技術(shù),就可能導(dǎo)致該區(qū)發(fā)生內(nèi)存溢出,一般該區(qū)發(fā)生內(nèi)存溢出時(shí)的錯(cuò)誤信息為:
outOfMemoryError:permgem space

jvm參數(shù):-XX:PermSize=2m -XX:MaxPermSize=2m  
將方法區(qū)的大小設(shè)置很低即可,在啟動(dòng)加載類庫(kù)時(shí)就會(huì)出現(xiàn)內(nèi)存不足的情況 

3、線程棧溢出(java.lang.StackOverflowError)
線程棧時(shí)線程獨(dú)有的一塊內(nèi)存結(jié)構(gòu),所以線程棧發(fā)生問題必定是某個(gè)線程運(yùn)行時(shí)產(chǎn)生的錯(cuò)誤。
一般線程棧溢出是由于遞歸太深或方法調(diào)用層級(jí)過多導(dǎo)致的。
發(fā)生棧溢出的錯(cuò)誤信息為:
java.lang.StackOverflowError

/** 
 * 線程操作棧溢出 
 * 
 * 參數(shù):-Xms5m -Xmx5m -Xmn2m -XX:NewSize=1m -Xss64k 
 * 
 */  
public class StackOverflowTest {  
    public static void main(String[] args) {  
        int i =0;  
        digui(i);  
    }  
    private static void digui(int i){  
        System.out.println(i++);  
        String[] s = new String[50];  
        digui(i);  
    }  
  
}  

五、為了避免內(nèi)存泄露,在編寫代碼的過程中可以參考下面的建議:

1、盡早釋放無(wú)用對(duì)象的引用

2、使用字符串處理,避免使用String,應(yīng)大量使用StringBuffer,每一個(gè)String對(duì)象都得獨(dú)立占用內(nèi)存一塊區(qū)域

3、盡量少用靜態(tài)變量,因?yàn)殪o態(tài)變量存放在永久代(方法區(qū)),永久代基本不參與垃圾回收

4、避免在循環(huán)中創(chuàng)建對(duì)象

5、開啟大型文件或從數(shù)據(jù)庫(kù)一次拿了太多的數(shù)據(jù)很容易造成內(nèi)存溢出,所以在這些地方要大概計(jì)算一下數(shù)據(jù)量的最大值是多少,并且設(shè)定所需最小及最大的內(nèi)存空間值。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,894評(píng)論 0 11
  • JVM內(nèi)存模型Java虛擬機(jī)(Java Virtual Machine=JVM)的內(nèi)存空間分為五個(gè)部分,分別是: ...
    光劍書架上的書閱讀 2,772評(píng)論 2 26
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,787評(píng)論 11 349
  • 原文閱讀 前言 這段時(shí)間懈怠了,罪過! 最近看到有同事也開始用上了微信公眾號(hào)寫博客了,挺好的~給他們點(diǎn)贊,這博客我...
    碼農(nóng)戲碼閱讀 6,144評(píng)論 2 31
  • 今天各大媒體被許家印許大老板刷屏,因?yàn)樗?5000000的年薪“包養(yǎng)”了任澤平,對(duì),你沒看錯(cuò),就是6個(gè)0。用財(cái)...
    青天日白閱讀 249評(píng)論 0 0

友情鏈接更多精彩內(nèi)容