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


當你從c&c++轉到一門具有垃圾回收功能的語言時,程序員的工作就會變得更加容易,因為你用完對象,他們會被自動回收,但是,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是不會釋放的,如下圖所示
這里寫圖片描述

從上圖中看以看出,如果棧先增長,在收縮,那么從棧中彈出的對象將不會被當作垃圾回收,即使程序不再使用棧中的這些隊象,他們也不會回收,因為棧中仍然保存這對象的引用,俗稱過期引用,這個內存泄露很隱蔽。

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會自動丟棄此值

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中的鍵。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM