1. 程式人生 > >記憶體洩漏和記憶體溢位(不看後悔,一看必懂)

記憶體洩漏和記憶體溢位(不看後悔,一看必懂)

​​​​​​​記憶體洩漏

記憶體洩漏是指不再被程式使用的物件或者變數還在記憶體中佔儲存空間

記憶體洩漏主要有兩種情況:一是在堆中申請的空間沒有被釋放;二是物件已經不再使用,但仍然在記憶體中保留著,垃圾回收器則無法保證不再使用的物件會被釋放.因此java語言中的記憶體洩漏主要指第二種情況(因為GC會解決第一種情況)

例如:

Vector v=new Vector(10);

for(int i=0;i<10;i++){

Object o =new Object();

v.add(o);

}

當迴圈退出後,o的作用域將會結束,但是由於v在使用這些物件,因此垃圾回收器無法將其回收,此時就造成了記憶體洩漏.只有將這些物件從Vector刪除後才能釋放建立的物件

引起記憶體洩漏的原因?

①靜態集合類,如HashMap和Vector.如果這些容器為靜態的,由於它們的生命週期和程式一致,那麼容器中的物件在程式結束之前不能被釋放,從而造成記憶體洩漏

②各種連線.如資料庫連線和IO連線等.在對資料庫進行操作的過程中,首先需要建立和資料庫的連線,當不再使用時需要呼叫close方法來釋放和資料庫的連線,只有連線被關閉後,垃圾回收器才會回收相應的物件

③監聽器.通常一個應用會用到多個監聽器,在釋放物件的同時如果沒有刪除監聽器,也有可能導致記憶體洩漏

④變數不合理的作用域.如果一個變數定義的作用範圍大於其使用範圍,很可能會造成記憶體洩漏,另一方面如果沒有及時地把物件設定為null,很有可能導致記憶體洩漏的發生

如:

class Server{

private String msg;

public void receiveMsg(){

readFromNet();//從網路中收到資料儲存到msg中

saveDB();    //把msg儲存到資料庫中

}

}

將msg的資訊儲存到資料庫後,此時msg已經沒用了,由於msg的生命週期和物件的宣告週期相同,此時msg還不能被回收,因此造成了記憶體洩漏.

解決方法

①將msg定義為區域性變數

②使用完msg後就把msg設定為null

⑤單例模式可能會造成記憶體洩漏.單例模式的實現方法有很多種,下例中所使用的單例模式就可能會造成記憶體洩漏

class BigClass{

}

class Singleton{

private BigClass bc;

private static Singleton instance=new Singleton(new BigClass());

private Singleton(BigClass bc){

this.bc=bc;

}

public Singleton getInstance(){

return instance;

}

}

Singleton存在一個對物件BigClass的引用,由於單例物件以靜態變數的方式儲存,因此它在jvm的整個生命週期中都存在,同時它有一個對物件BigClass的引用,這樣會導致BigClass類的物件不能被回收

​​​​​​​記憶體溢位

程式需要的記憶體超過了系統所能分配的範圍.記憶體洩漏時記憶體溢位的誘因,但不是唯一因素.將丟擲OutOfMemoryError異常

記憶體溢位的原因

①記憶體洩漏的原因

②堆記憶體溢位(OutOfMemoryError存放的物件過多)

③方法區溢位(OutOfMemoryError載入的類過多,或者使用反射、cglib等這種動態代理生成類的技術,就可能導致該區發生記憶體溢位)

④執行緒棧溢位(StackOverflowError執行緒棧時執行緒獨有的一塊記憶體結構,所以執行緒棧發生問題必定是某個執行緒執行時產生的錯誤。 一般執行緒棧溢位是由於遞迴太深或方法呼叫層級過多導致的。)

記憶體洩漏的解決辦法

1.修改JVM啟動引數,直接增加記憶體。(-Xms,-Xms引數一定不要忘記加) 2.檢查錯誤日誌,檢視“OutOfMemory”錯誤前是否有其他異常或錯誤。 3.對程式碼進行走查和分析,找出可能發生記憶體溢位的位置。