當你從c&c++轉到一門具備垃圾回收功能的語言時,程序員的工做就會變得更加容易,由於你用完對象,他們會被自動回收,可是,java程序員真的不須要考慮內存泄露嗎? 其實否則java
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); } }
上述程序並無明顯的錯誤,可是這段程序有一個內存泄漏,隨着GC活動的增長,或者內存佔用的不斷增長,程序性能的下降就會表現出來,嚴重時可致使內存泄漏,可是這種失敗狀況相對較少。
代碼的主要問題在pop函數,下面經過這張圖示展示
假設這個棧一直增加,增加後以下圖所示
當進行大量的pop操做時,因爲引用未進行置空,gc是不會釋放的,以下圖所示
c++
從上圖中看以看出,若是棧先增加,在收縮,那麼從棧中彈出的對象將不會被看成垃圾回收,即便程序再也不使用棧中的這些隊象,他們也不會回收,由於棧中仍然保存這對象的引用,俗稱過時引用,這個內存泄露很隱蔽。程序員
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; return result; }
一旦引用過時,清空這些引用,將引用置空。
緩存
內存泄漏的另外一個常見來源是緩存,一旦你把對象引用放入到緩存中,他就很容易遺忘,對於這個問題,能夠使用WeakHashMap表明緩存,此種Map的特色是,當除了自身有對key的引用外,此key沒有其餘引用那麼此map會自動丟棄此值socket
/** * 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中的鍵。性能