OOM,全稱「Out Of Memory」,翻譯成中文就是「內存用完了」,來源於java.lang.OutOfMemoryError。java
看下關於的官方說明:Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. 意思就是說,當JVM由於沒有足夠的內存來爲對象分配空間而且垃圾回收器也已經沒有空間可回收時,就會拋出這個error,關於Java內存模型整理了一份+筆記,地址:Java後端面試真題。面試
(注:非exception,由於這個問題已經嚴重到不足以被應用處理)。數據庫
如HashMap、LinkedList等等。若是這些容器爲靜態的,那麼它們的生命週期與程序一致,則容器中的對象在程序結束以前將不能被釋放,從而形成內存泄漏。簡單而言,長生命週期的對象持有短生命週期對象的引用,儘管短生命週期的對象再也不使用,可是由於長生命週期對象持有它的引用而致使不能被回收。後端
各類鏈接,如數據庫鏈接、網絡鏈接和IO鏈接等緩存
在對數據庫進行操做的過程當中,首先須要創建與數據庫的鏈接,當再也不使用時,須要調用close方法來釋放與數據庫的鏈接。只有鏈接被關閉後,垃圾回收器纔會回收對應的對象。不然,若是在訪問數據庫的過程當中,對Connection、Statement或ResultSet不顯性地關閉,將會形成大量的對象沒法被回收,從而引發內存泄漏。網絡
通常而言,一個變量的定義的做用範圍大於其使用範圍,頗有可能會形成內存泄漏。另外一方面,若是沒有及時地把對象設置爲null,頗有可能致使內存泄漏的發生。dom
public class UsingRandom {private String msg;public void receiveMsg() { readFromNet(); // 從網絡中接受數據保存到msg中 saveDB(); // 把msg保存到數據庫中 }}
如上面這個僞代碼,經過readFromNet方法把接受的消息保存在變量msg中,而後調用saveDB方法把msg的內容保存到數據庫中,此時msg已經就沒用了,因爲msg的生命週期與對象的生命週期相同,此時msg還不能回收,所以形成了內存泄漏。socket
實際上這個msg變量能夠放在receiveMsg方法內部,當方法使用完,那麼msg的生命週期也就結束,此時就能夠回收了。還有一種方法,在使用完msg後,把msg設置爲null,這樣垃圾回收器也會回收msg的內存空間。ide
若是一個外部類的實例對象的方法返回了一個內部類的實例對象,這個內部類對象被長期引用了,即便那個外部類實例對象再也不被使用,但因爲內部類持有外部類的實例對象,這個外部類對象將不會被垃圾回收,這也會形成內存泄露。函數
當一個對象被存儲進HashSet集合中之後,就不能修改這個對象中的那些參與計算哈希值的字段了,不然,對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不一樣了,在這種狀況下,即便在contains方法使用該對象的當前引用做爲的參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會致使沒法從HashSet集合中單獨刪除當前對象,形成內存泄露
舉個例子-看你可否找出內存泄漏
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是不會釋放的,以下圖所示
從上圖中看以看出,若是棧先增加,在收縮,那麼從棧中彈出的對象將不會被看成垃圾回收,即便程序再也不使用棧中的這些對象,他們也不會回收,由於棧中仍然保存這對象的引用,俗稱過時引用,這個內存泄露很隱蔽。
解決方法
public Object pop() {if (size == 0)throw new EmptyStackException();Object result = elements[--size]; elements[size] = null;return result;}
一旦引用過時,清空這些引用,將引用置空。
內存泄漏的另外一個常見來源是緩存,一旦你把對象引用放入到緩存中,他就很容易遺忘,對於這個問題,可使用WeakHashMap表明緩存,此種Map的特色是,當除了自身有對key的引用外,此key沒有其餘引用那麼此map會自動丟棄此值
1.代碼示例
package com.ratel.test;/** * @業務描述: * @package_name:com.ratel.test * @project_name:ssm * @author:ratelfu@qq.com * @copyright (c) ratelfu 版權全部 */import java.util.HashMap;import java.util.Map;import java.util.WeakHashMap;import java.util.concurrent.TimeUnit;public class MapTest {static Map wMap = new WeakHashMap();static Map map = new HashMap();public static void main(String[] args) { init(); testWeakHashMap(); testHashMap(); }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); } }}/** 結果 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中的鍵。