JVM - 內存泄露、內存溢出

內存泄露 

指程序中動態分配內存給一些臨時對象,可是對象不會被GC所回收,它始終佔用內存。即被分配的對象可達但已無用。java

內存溢出 

指程序運行過程當中沒法申請到足夠的內存而致使的一種錯誤。內存溢出一般發生於OLD段或Perm段垃圾回收後,仍然無內存空間容納新的Java對象的狀況。數據庫

從定義上能夠看出內存泄露是內存溢出的一種誘因,不是惟一因素。
Java 堆內存的OutOfMemoryError異常是實際應用中最多見的內存溢出異常狀況。
出現Java 堆內存溢出時,異常堆棧信息「java.lang.OutOfMemoryError」會跟着進一步提示「Java heapspace」。數組


內存泄露的常見場景

一、長生命週期的對象持有短生命週期對象的引用
這是內存泄露最多見的場景,也是代碼設計中常常出現的問題。例如:在全局靜態map中緩存局部變量,且沒有清空操做,隨着時間的推移,這個map會愈來愈大,形成內存泄露。緩存

二、修改hashset中對象的參數值,且參數是計算哈希值的字段 
當一個對象被存儲進HashSet集合中之後,就不能修改這個對象中的那些參與計算哈希值的字段,不然對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不一樣了,在這種狀況下,即便在contains方法使用該對象的當前引用做爲參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會致使沒法從HashSet集合中刪除當前對象,形成內存泄露。工具

三、機器的鏈接數和關閉時間設置 
長時間開啓很是耗費資源的鏈接,也會形成內存泄露。spa


內存溢出的常見場景

一、堆內存溢出(outOfMemoryError:Java heap space) 
堆中的內存是用來生成對象實例和數組的。線程

二、方法區內存溢出(outOfMemoryError:permgem space) 
方法區主要存放的是類信息、常量、靜態變量等。
因此若是程序加載的類過多,或者使用反射、gclib等這種動態代理生成類的技術,就可能致使該區發生內存溢出,通常該區發生內存溢出時的錯誤信息爲: outOfMemoryError:permgem space 設計

三、線程棧溢出(java.lang.StackOverflowError) 
線程棧時線程獨有的一塊內存結構,因此線程棧發生問題一定是某個線程運行時產生的錯誤。 
通常線程棧溢出是因爲遞歸太深或方法調用層級過多致使的。 發生棧溢出的錯誤信息爲: java.lang.StackOverflowError代理


分析方法

通常的手段是首先經過內存映像分析工具(如Eclipse Memory Analyzer)對dump 出來的堆轉儲快照進行分析,
重點是確認內存中的對象是不是必要的,也就是要先分清楚究竟是出現了內存泄漏(Memory Leak)仍是內存溢出(Memory Overflow)。對象

若是是內存泄漏,可進一步經過工具查看泄漏對象到GC Roots 的引用鏈。
因而就能找到泄漏對象是經過怎樣的路徑與GC Roots 相關聯並致使垃圾收集器沒法自動回收它們的。
掌握了泄漏對象的類型信息,以及GC Roots 引用鏈的信息,就能夠比較準確地定位出泄漏代碼的位置。

若是不存在泄漏,換句話說就是內存中的對象確實都還必須存活着,那就應當檢查虛擬機的堆參數(-Xmx 與-Xms),與機器物理內存對比看是否還能夠調大,
從代碼上檢查是否存在某些對象生命週期過長、持有狀態時間過長的狀況,嘗試減小程序運行期的內存消耗。


建議

1.儘早釋放無用對象的引用 2.使用字符串處理,避免使用String,應大量使用StringBuffer,每個String對象都得獨立佔用內存一塊區域 3.儘可能少用靜態變量,由於靜態變量存放在永久代(方法區),永久代基本不參與垃圾回收 4.避免在循環中建立對象 5.開啓大型文件或從數據庫一次拿了太多的數據很容易形成內存溢出,因此在這些地方要大概計算一下數據量的最大值是多少,而且設定所需最小及最大的內存空間值。

相關文章
相關標籤/搜索