1. 程式人生 > >Jvm記憶體洩漏

Jvm記憶體洩漏

記憶體洩漏和記憶體溢位的關係

記憶體洩露:指程式中動態分配記憶體給一些臨時物件,但是物件不會被GC所回收,它始終佔用記憶體。即被分配的物件可達但已無用。
記憶體溢位:指程式執行過程中無法申請到足夠的記憶體而導致的一種錯誤。記憶體溢位通常發生於OLD段或Perm段垃圾回收後,仍然無記憶體空間容納新的Java物件的情況。
從定義上可以看出記憶體洩露是記憶體溢位的一種誘因,但是不是唯一因素。

可以使用Runtime.getRuntime().freeMemory()進行記憶體洩漏查詢

Runtime.getRuntime().freeMemory()表示當前還有多少空閒記憶體

package com.one.util;

public class Hello {
    public static void main(String[] args) {
        System.out.println("free記憶體:" + Runtime.getRuntime().freeMemory() / 1024
            / 1024);
        String[] aaa = new String[2000000];
        for (int i = 0; i < 2000000; i++) {
            aaa[i] = new String("aaa");
        }
        System.out.println("free記憶體:" + Runtime.getRuntime().freeMemory() / 1024 / 1024);
    }
}

此時結果如下所示

記憶體洩漏的例子

如果長生命週期的物件持有短生命週期的引用,就很可能會出現記憶體洩露

比如下面的程式碼,這裡的object例項,其實我們期望它只作用於method1()方法中,且其他地方不會再用到它,但是,當method1()方法執行完成後,object物件所分配的記憶體不會馬上被認為是可以被釋放的物件,只有在Simple類建立的物件被釋放後才會被釋放,嚴格的說,這就是一種記憶體洩露。

public class Simple {
 
    Object object;
 
    public void method1(){
        object = new Object();
        //...其他程式碼
    }
}

怎麼解決上面的問題呢,加上下面的藍色程式碼註釋就好了

public class Simple {
 
    Object object;
 
    public void method1(){
        object = new Object();
        //...其他程式碼
        // 藍色程式碼註釋開始
        object = null;
        // 藍色程式碼註釋結束
    }
}

集合裡面的記憶體洩漏

集合裡面的資料都設定成null,但是集合記憶體還是存在的

比如下面的程式碼
因為你已經在下面的藍色程式碼註釋裡面進行company=null了,所以下面的list集合裡面的資料都是無用的了,但是此時list集合裡面的所有元素都不會進行垃圾回收

package com.four;

import java.util.ArrayList;
import java.util.List;

public class Hello {
    public static void main(String[] args) {
        List<Company> list = new ArrayList<Company>();
        int i=0;
        for(int j=0;j<10;j++){
            Company company = new Company();
            company.setName("ali");
            list.add(company);
            // 藍色程式碼註釋開始
            company = null;
            // 藍色程式碼註釋結束
        }

        System.gc();
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("已經測試了"+(++i)+"秒");
        }
    }

}


class Company {
    private String name;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("回收Comapny");
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

怎麼解決上面的問題呢,就是把上面的list集合變數也變成null,比如加上下面的紅色程式碼註釋

package com.one.util;

import java.util.ArrayList;
import java.util.List;

public class Hello {
    public static void main(String[] args) {
        List<Company> list = new ArrayList<Company>();
        int i = 0;
        for (int j = 0; j < 10; j++) {
            Company company = new Company();
            company.setName("ali");
            list.add(company);
            // 藍色程式碼註釋開始
            company = null;
            // 藍色程式碼註釋結束
        }
        // 紅色程式碼註釋開始
        list = null;
       // 紅色程式碼註釋結束
        System.gc();
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("已經測試了" + (++i) + "秒");
        }
    }

}

class Company {
    private String name;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("回收Comapny");
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

此時結果如下所示,可以看出來集合裡面的Company變數都回收了

還有就是使用remove()方法進行移除元素的時候,也可能會造成記憶體洩漏

什麼意思呢,
就比如ArrayList裡面的pop(),如果是下面的寫法就會造成記憶體洩漏,因為下面的elementData[--size]這個元素移除之後,並沒有進行設定成null

public E pop(){
    if(size == 0)
        return null;
    else
        return (E) elementData[size];
}

所以上面的程式碼應該變成下面這樣,此時注意下面的藍色程式碼註釋裡面的size值比下面的紅色程式碼註釋裡面的size小1

public E pop(){
    if(size == 0)
        return null;
    else{
        // 紅色程式碼註釋開始
        E e = (E) elementData[--size];
        // 紅色程式碼註釋結束
        // 藍色程式碼註釋開始
        elementData[size] = null;
        // 藍色程式碼註釋結束
        return e;
    }
}

連線沒有關閉會洩漏

比如資料庫連線(dataSourse.getConnection()),網路連線(socket)和io連線,這些連結在使用的時候,除非顯式的呼叫了其close()方法(或類似方法)將其連線關閉,否則是不會自動被GC回收的。其實原因依然是長生命週期物件持有短生命週期物件的引用。所以我們經常在網上看到在連線呼叫結束的時候要進行呼叫close()進行關閉,這樣可以回收不用的記憶體物件,增加可用記憶體。

能看到這裡的同學,就幫忙點個推薦吧吧,Thanks♪(・ω・)ノ

原文鏈