您好,登錄后才能下訂單哦!
內存泄漏(Memory Leak)
概念
內存泄漏(Memory Leak)是指程序中己動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。
發生條件
內存泄漏必須滿足以下兩個條件
對象是可達的。即在有向圖中,存在通道達到該對象,GC不會回收
對象是無用的。即程序以后不會再使用該對象
發生場景
靜態集合類引起內存泄漏
HashMap、Vactor等集合的使用最容易出現內存泄漏。因為這些集合屬于靜態集合,這些靜態變量的生命周期和應用程序一致,他們所引用的所有Object對象都不能被釋放,因為這些對象還一直被Vector引用著
Static Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object(); //每次創建新的對象
v.add(o);
o = null; //將對象添加到集合后將對象的引用置空
}
//因為對象的引用置空之后,JVM已經失去的使用該對象的價值,本應該被GC清除,但是在vector集合中還存在著此對象的引用,導致沒能順利清除
循環申請Object 對象,并將所申請的對象放入一個Vector 中,如果僅僅釋放引用本身(o=null),那么Vector 仍然引用該對象,所以這個對象對GC 來說是不可回收的。因此,如果對象加入到Vector 后,還必須從Vector 中刪除,最簡單的方法就是將v = null。這樣就可以將Vector執行那個的對象也釋放。
當集合(Hash算法的集合)里面的對象屬性被修改后,再調用remove()方法時不起作用
public static void main(String[] args) {
Set set = new HashSet();
Person p1 = new Person("唐僧", "pwd1", 25);
Person p2 = new Person("孫悟空", "pwd2", 26);
Person p3 = new Person("豬八戒", "pwd3", 27);
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println("總共有:" + set.size() + " 個元素!"); //結果:總共有:3 個元素!
p3.setAge(2); //修改p3的年齡,此時p3元素對應的hashcode值發生改變
set.remove(p3); //此時remove不掉,造成內存泄漏
set.add(p3); //重新添加,居然添加成功
System.out.println("總共有:" + set.size() + " 個元素!"); //結果:總共有:4 個元素!
for (Person person : set) {
System.out.println(person);
System.out.println(person.hashCode());
}
}
監聽器
在java 編程中,我們都需要和監聽器打交道,通常一個應用當中會用到很多監聽器,我們會調用一個控件的諸如addXXXListener()等方法來增加監聽器,但往往在釋放對象的時候卻沒有記住去刪除這些監聽器,從而增加了內存泄漏的機會。
各種連接
比如數據庫連接(dataSourse.getConnection()),網絡連接(socket)和io連接,除非其顯式的調用了其close()方法將其連接關閉,否則是不會自動被GC 回收的。對于Resultset 和Statement 對象可以不進行顯式回收,但Connection 一定要顯式回收,因為Connection 在任何時候都無法自動回收,而Connection一旦回收,Resultset 和Statement 對象就會立即為NULL。但是如果使用連接池,情況就不一樣了,除了要顯式地關閉連接,還必須顯式地關閉Resultset Statement 對象(關閉其中一個,另外一個也會關閉),否則就會造成大量的Statement 對象無法釋放,從而引起內存泄漏。這種情況下一般都會在try里面去的連接,在finally里面釋放連接。
單例模式
如果單例對象持有外部對象的引用,那么這個外部對象將不能被jvm正常回收,導致內存泄露。
不正確使用單例模式是引起內存泄露的一個常見問題,單例對象在被初始化后將在JVM的整個生命周期中存在(以靜態變量的方式),如果單例對象持有外部對象的引用,那么這個外部對象將不能被jvm正常回收,導致內存泄露,考慮下面的例子:
class A {
public A() {
B.getInstance().setA(this);
}
....
}
//B類采用單例模式
class B {
private A a;
private static B instance = new B();
public B() {
}
public static B getInstance() {
return instance;
}
public void setA(A a) {
this.a = a;
}
//getter...
}
顯然B采用singleton模式,它持有一個A對象的引用,而這個A類的對象將不能被回收。想象下如果A是個比較復雜的對象或者集合類型會發生什么情況
分類鄭州婦科醫院 http://www.zyfuke.com/
1. 常發性內存泄漏。 發生內存泄漏的代碼會被多次執行到,每次被執行的時候都會導致一塊內存泄漏。
2. 偶發性內存泄漏。 發生內存泄漏的代碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對于特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測內存泄漏至關重要。
3. 一次性內存泄漏。 發生內存泄漏的代碼只會被執行一次,或者由于算法上的缺陷,導致總會有一塊僅且一塊內存發生泄漏。比如,在類的構造函數中分配內存,在析構函數中卻沒有釋放該內存,所以內存泄漏只會發生一次。
4. 隱式內存泄漏。 程序在運行過程中不停的分配內存,但是直到結束的時候才釋放內存。嚴格的說這里并沒有發生內存泄漏,因為最終程序釋放了所有申請的內存。但是對于一個服務器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。所以,我們稱這類內存泄漏為隱式內存泄漏。
內存溢出(Out Of Memory)
概念
程序在申請內存時,沒有足夠的內存空間供其使用
內存溢出已經是軟件開發歷史上存在了近40年的“老大難”問題,像在“紅色代碼”病毒事件中表現的那樣,它已經成為黑客攻擊企業網絡的“罪魁禍首”。 如在一個域中輸入的數據超過了它的要求就會引發數據溢出問題,多余的數據就可以作為指令在計算機上運行。據有關安全小組稱,操作系統中超過50%的安全漏洞都是由內存溢出引起的,其中大多數與微軟的技術有關。內存溢出錯誤是大數據處理平臺的常見錯誤,例如,國際知名的程序開發者問答網站 stackoverflow 上關于“Hadoop out of memory”的問題超過10000個,在Spark郵件列表上有10%的問題是關于“out of memory”。 內存溢出錯誤會導致處理數據的任務失敗,甚至會引發平臺崩潰等嚴重后果。對于內存溢出大部分的處理方法是重新執行任務,然而, 對于由系統配置、數據流、用戶代碼等原因而導致的內存溢出錯誤,即使用戶重新執行任務依然無法避免
發生條件
內存中加載的數據過于龐大,如一次性從數據庫取出過多的數據
集合類中,有對對象的引用,使用完后未清空,使得JVM不能不嫩回收
代碼中存在死循環或循環產生過多重復的對象實體
使用的第三方軟件存在bug
啟動參數內存值設置的過小
分類
OutOfMemoryError: PermGen space
PermGen Space指的是內存的永久保存區,該塊內存主要是被JVM用來存放class和meta信息的,當class被加載loader的時候就會被存儲到該內存區中,與存放類的實例的heap區不同,java中的垃圾回收器GC不會在主程序運行期對PermGen space進行清理。
因此,程序啟動時如果需要加載的信息太多,超出這個空間的大小,則會發生溢出。
解決方案: 增加空間分配——增加java虛擬機中的XX:PermSize和XX:MaxPermSize參數的大小,其中XX:PermSize是初始永久保存區域大小,XX:MaxPermSize是最大永久保存區域大小。
OutOfMemoryError:Java heap space
heap space是Java內存中的堆區,主要用來存放對象,當對象太多超出了空間大小,GC又來不及釋放的時候,就會發生溢出錯誤。即內存泄露越來越嚴重時,可能會發生內存溢出。
解決方案:(1)、檢查程序,減少大量重復創建對象的死循環,減少內存泄露。
(2)、增加Java虛擬機中Xms(初始堆大小)和Xmx(最大堆大小)參數的大小。
StackOverFlowError
stack是Java內存中的棧空間,主要用來存放方法中的變量,參數等臨時性的數據的,發生溢出一般是因為分配空間太小,或是執行的方法遞歸層數太多創建了占用了太多棧幀導致溢出。
解決方案: 修改配置參數-Xss參數增加線程棧大小之外,優化程序是尤其重要。
OOM排查思路
第一步,修改JVM啟動參數,直接增加內存。(-Xms,-Xmx參數一定不要忘記加。)
第二步,檢查錯誤日志,查看“OutOfMemery”錯誤前,是否有其他異常或錯誤
第三步,對代碼進行走查分析,找出可能發生內存溢出的位置
重點排查以下幾點:
1.檢查對數據庫查詢中,是否有一次獲得全部數據的查詢。一般來說,如果一次取十萬條記錄到內存,就可能引起內存溢出。這個問題比較隱蔽,在上線前,數據庫中數據較少,不容易出問題,上線后,數據庫中數據多了,一次查詢就有可能引起內存溢出。因此對于數據庫查詢盡量采用分頁的方式查詢。
2.檢查代碼中是否有死循環或遞歸調用。
3.檢查是否有大循環重復產生新對象實體。
4.檢查List、MAP等集合對象是否有使用完后,未清除的問題。List、MAP等集合對象會始終存有對對象的引用,使得這些對象不能被GC回收。
第四步,使用內存查看工具動態查看內存使用情況
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。