java程序員--當心你代碼中的內存泄漏

當你從c&c++轉到一門具備垃圾回收功能的語言時,程序員的工做就會變得更加容易,由於你用完對象,他們會被自動回收,可是,java程序員真的不須要考慮內存泄露嗎? 其實否則java

1.舉個例子-看你可否找出內存泄漏

import java.util.Arrays;

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        return elements[--size];
    }

    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

1.1緣由分析

上述程序並無明顯的錯誤,可是這段程序有一個內存泄漏,隨着GC活動的增長,或者內存佔用的不斷增長,程序性能的下降就會表現出來,嚴重時可致使內存泄漏,可是這種失敗狀況相對較少。
代碼的主要問題在pop函數,下面經過這張圖示展示
假設這個棧一直增加,增加後以下圖所示
這裏寫圖片描述
當進行大量的pop操做時,因爲引用未進行置空,gc是不會釋放的,以下圖所示
這裏寫圖片描述c++

從上圖中看以看出,若是棧先增加,在收縮,那麼從棧中彈出的對象將不會被看成垃圾回收,即便程序再也不使用棧中的這些隊象,他們也不會回收,由於棧中仍然保存這對象的引用,俗稱過時引用,這個內存泄露很隱蔽。程序員

1.2解決方法

public Object pop() {
    if (size == 0)
    throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null;
    return result;
}

一旦引用過時,清空這些引用,將引用置空。
這裏寫圖片描述緩存

2.緩存泄漏

內存泄漏的另外一個常見來源是緩存,一旦你把對象引用放入到緩存中,他就很容易遺忘,對於這個問題,能夠使用WeakHashMap表明緩存,此種Map的特色是,當除了自身有對key的引用外,此key沒有其餘引用那麼此map會自動丟棄此值socket

2.1代碼示例

/**
 * Created by liuroy on 2017/2/25.
 */
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;

public class Test {
    static Map wMap = new WeakHashMap();
    static Map map = new HashMap();
    public static void init(){
        String ref1= new String("obejct1");
        String ref2 = new String("obejct2");
        String ref3 = new String ("obejct3");
        String ref4 = new String ("obejct4");
        wMap.put(ref1, "chaheObject1");
        wMap.put(ref2, "chaheObject2");
        map.put(ref3, "chaheObject3");
        map.put(ref4, "chaheObject4");
        System.out.println("String引用ref1,ref2,ref3,ref4 消失");

    }
    public static void testWeakHashMap(){

        System.out.println("WeakHashMap GC以前");
        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }
        try {
            System.gc();
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("WeakHashMap GC以後");
        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }
    }
    public static void testHashMap(){
        System.out.println("HashMap GC以前");
        for (Object o : map.entrySet()) {
            System.out.println(o);
        }
        try {
            System.gc();
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("HashMap GC以後");
        for (Object o : map.entrySet()) {
            System.out.println(o);
        }
    }
    public static void main(String[] args) {
        init();
        testWeakHashMap();
        testHashMap();
    }
}
/** 結果
String引用ref1,ref2,ref3,ref4 消失
WeakHashMap GC以前
obejct2=chaheObject2
obejct1=chaheObject1
WeakHashMap GC以後
HashMap GC以前
obejct4=chaheObject4
obejct3=chaheObject3
Disconnected from the target VM, address: '127.0.0.1:51628', transport: 'socket'
HashMap GC以後
obejct4=chaheObject4
obejct3=chaheObject3
**/

這裏寫圖片描述
上面代碼和圖示主演演示WeakHashMap如何自動釋放緩存對象,當init函數執行完成後,局部變量字符串引用weakd1,weakd2,d1,d2都會消失,此時只有靜態map中保存中對字符串對象的引用,能夠看到,調用gc以後,hashmap的沒有被回收,而WeakHashmap裏面的緩存被回收了。函數

監聽器和回調

內存泄漏第三個常見來源是監聽器和其餘回調,若是客戶端在你實現的API中註冊回調,卻沒有顯示的取消,那麼就會積聚。須要確保回調當即被看成垃圾回收的最佳方法是隻保存他的若引用,例如將他們保存成爲WeakHashMap中的鍵。性能

相關文章
相關標籤/搜索