java面試:內存泄漏相關知識

百度百科(https://baike.baidu.com/item/內存泄漏/6181425?fr=aladdin):內存泄漏(Memory Leak)是指程序中己動態分配的堆內存由於某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重後果。

一般來說,內存泄漏存在兩種情況:

       :如在C/C++語言中的,在堆中分配的內存,在還沒有將其釋放掉的時候,就把所有能訪問這塊內存的方式都刪掉(如指針重新賦值);

       :則是在內存對象明明已經不需要的時候,還仍然保留着這塊內存和它的訪問方式(引用)。

       java通過垃圾收集器(GC)自動管理內存的回收,而不需要程序員自己來釋放內存。理論上java中所有不被利用的對象所佔有的內存,它們都會被GC回收,但事實java也存在內存泄漏,但它的表現與c++不同。

       第一種情況,在Java種有垃圾回收機制(GC)的引入,已經得到了很好的解決。所以,Java中的內存泄漏主要指的是第二種情況,而C++則包含以上兩種情況。

        在不涉及複雜數據結構的一般情況下,Java 的內存泄露表現爲一個內存對象的生命週期超出了程序需要它的時間長度。我們有時也將其稱爲“對象遊離”。

        內存泄漏是造成應用程序OOM(OutOfMemory)的主要原因之一。我們知道Android系統爲每個應用程序分配的內存是有限的,而當一個應用中產生的內存泄漏比較多時,這就難免會導致應用所需要的內存超過系統分配的內存限額,這就造成了內存溢出從而導致應用Crash。

       長生命週期的對象持有短生命週期對象的引用就很可能發生內存泄露,儘管短生命週期對象已經不再需要,但是因爲長生命週期對象持有它的引用而導致不能被回收。

常見的內存泄漏及解決方法:

1、不正確使用單例模式是引起內存泄露的一個常見問題,單例對象在被初始化後將在JVM的整個生命週期中存在(以靜態變量的方式),如果單例對象持有外部對象的引用,那麼這個外部對象將不能被jvm正常回收,導致內存泄露,

2、各種連接 ,比如數據庫連接(dataSourse.getConnection()),網絡連接(socket)和io連接,除非其顯式的調用了其close()方法將其連接關閉,否則是不會自動被GC 回收的。數據庫連接中對於Resultset 和Statement 對象可以不進行顯式回收,但Connection 一定要顯式回收,因爲Connection 在任何時候都無法自動回收,而Connection一旦回收,Resultset 和Statement 對象就會立即爲NULL。如果使用連接池,情況就不一樣了,除了要顯式地關閉連接,還必須顯式地關閉Resultset Statement 對象(關閉其中一個,另外一個也會關閉),否則就會造成大量的Statement 對象無法釋放,從而引起內存泄漏。這種情況下一般都會在try裏面去的連接,在finally裏面釋放連接。

3、內部類和外部模塊等的引用,內部類(匿名內部類、非靜態內部類)的引用是比較容易遺忘的一種,而且一旦沒釋放可能導致一系列的後繼類對象沒有釋放。外部模塊,比如在A模塊調用了B模塊的一個方法public void login(Object b){},傳入了一個對象,很可能模塊A就保持了對該對象的引用,這時候就需要注意模塊A 是否提供相應的操作去除引用。

4、監聽器 在java 編程中,我們都需要和監聽器打交道,通常一個應用當中會用到很多監聽器,我們會調用一個控件的諸如addXXXListener()等方法來增加監聽器,但往往在釋放對象的時候卻沒有記住去刪除這些監聽器,從而增加了內存泄漏的機會。

5、靜態集合類 靜態變量生命週期與應用生命週期一樣,他們集合中所引用的對象也一直不能被釋放。必須將靜態集合對象內容清空,才能釋放所引用的對象。

6、當集合裏面的對象屬性被修改後,該對象的hashcode值發生改變,再調用remove()方法時不起作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章