Java內存溢出和內存泄露

轉載於:https://blog.csdn.net/shimiso/article/details/21830871html

 

雖然jvm能夠經過GC自動回收無用的內存,可是代碼很差的話仍然存在內存溢出的風險。java

最近在網上搜集了一些資料,現整理以下:數據庫

 

1、爲何要了解內存泄露和內存溢出?數組

 

一、內存泄露通常是代碼設計存在缺陷致使的,經過了解內存泄露的場景,能夠避免沒必要要的內存溢出和提升本身的代碼編寫水平;緩存

 

二、經過了解內存溢出的幾種常見狀況,能夠在出現內存溢出的時候快速的定位問題的位置,縮短解決故障的時間。jvm

 

 

 2、基本概念ui

 

理解這兩個概念很是重要。spa

 

內存泄露:指程序中動態分配內存給一些臨時對象,可是對象不會被GC所回收,它始終佔用內存。即被分配的對象可達但已無用。.net

 

內存溢出:指程序運行過程當中沒法申請到足夠的內存而致使的一種錯誤。內存溢出一般發生於OLD段或Perm段垃圾回收後,仍然無內存空間容納新的Java對象的狀況。線程

 

從定義上能夠看出內存泄露是內存溢出的一種誘因,不是惟一因素。

 

 

3、內存泄露的幾種場景:

 

一、長生命週期的對象持有短生命週期對象的引用

 

            這是內存泄露最多見的場景,也是代碼設計中常常出現的問題。

            例如:在全局靜態map中緩存局部變量,且沒有清空操做,隨着時間的推移,這個map會愈來愈大,形成內存泄露。

 

二、修改hashset中對象的參數值,且參數是計算哈希值的字段

 

             當一個對象被存儲進HashSet集合中之後,就不能修改這個對象中的那些參與計算哈希值的字段,不然對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不一樣了,在這種狀況下,即便在contains方法使用該對象的當前引用做爲參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會致使沒法從HashSet集合中刪除當前對象,形成內存泄露。

 

三、機器的鏈接數和關閉時間設置

 

            長時間開啓很是耗費資源的鏈接,也會形成內存泄露。

 

 

 

 4、內存溢出的幾種狀況:

 

一、堆內存溢出(outOfMemoryError:java heap space)

       在jvm規範中,堆中的內存是用來生成對象實例和數組的。

       若是細分,堆內存還能夠分爲年輕代和年老代,年輕代包括一個eden區和兩個survivor區。

       當生成新對象時,內存的申請過程以下:

          a、jvm先嚐試在eden區分配新建對象所需的內存;

          b、若是內存大小足夠,申請結束,不然下一步;

          c、jvm啓動youngGC,試圖將eden區中不活躍的對象釋放掉,釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區;

          d、Survivor區被用來做爲Eden及old的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,不然會被保留在Survivor區;

          e、 當OLD區空間不夠時,JVM會在OLD區進行full GC;

          f、full GC後,若Survivor及OLD區仍然沒法存放從Eden複製過來的部分對象,致使JVM沒法在Eden區爲新對象建立內存區域,則出現」out of memory錯誤」:

                                   outOfMemoryError:java heap space

 

代碼舉例:

/**
 * 堆內存溢出
 *
 * jvm參數:-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);
            }
        }
    }
}

 

           

二、方法區內存溢出(outOfMemoryError:permgem space)

       在jvm規範中,方法區主要存放的是類信息、常量、靜態變量等。

       因此若是程序加載的類過多,或者使用反射、gclib等這種動態代理生成類的技術,就可能致使該區發生內存溢出,通常該區發生內存溢出時的錯誤信息爲:

             outOfMemoryError:permgem space

 

代碼舉例:

 

  1.  
    jvm參數:-XX:PermSize= 2m -XX:MaxPermSize=2m
  2.  
    將方法區的大小設置很低便可,在啓動加載類庫時就會出現內存不足的狀況



 

 

三、線程棧溢出(java.lang.StackOverflowError)

       線程棧時線程獨有的一塊內存結構,因此線程棧發生問題一定是某個線程運行時產生的錯誤。

       通常線程棧溢出是因爲遞歸太深或方法調用層級過多致使的。

       發生棧溢出的錯誤信息爲:

              java.lang.StackOverflowError

 

代碼舉例:

 

/**
 * 線程操做棧溢出
 *
 * 參數:-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);
    }
 
}

 

5、爲了不內存泄露,在編寫代碼的過程當中能夠參考下面的建議:

 

一、儘早釋放無用對象的引用

 

二、使用字符串處理,避免使用String,應大量使用StringBuffer,每個String對象都得獨立佔用內存一塊區域

 

三、儘可能少用靜態變量,由於靜態變量存放在永久代(方法區),永久代基本不參與垃圾回收

 

四、避免在循環中建立對象

 

五、開啓大型文件或從數據庫一次拿了太多的數據很容易形成內存溢出,因此在這些地方要大概計算一下數據量的最大值是多少,而且設定所需最小及最大的內存空間值。 

 

 

參考:

 

http://zhidao.baidu.com/question/263477119.html    

https://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/  Java的內存泄漏

http://jelly-x.iteye.com/blog/1120406 JVM內存分析及致使內存溢出

http://wenku.baidu.com/view/ef3158fc04a1b0717fd5ddda.html java內存泄露和內存溢出

相關文章
相關標籤/搜索