1. 程式人生 > >java有自己的記憶體回收機制,但為什麼還存在記憶體洩漏的問題?

java有自己的記憶體回收機制,但為什麼還存在記憶體洩漏的問題?

原文地址:https://www.cnblogs.com/panxuejun/p/5888817.html

1.既然 Java 的垃圾回收機制能夠自動的回收記憶體,怎麼還會出現記憶體洩漏的情況呢?
這個問題,我們需要知道 GC 在什麼時候回收記憶體物件,什麼樣的記憶體物件會被 GC 認為是“不再使用”的。

Java中對記憶體物件的訪問,使用的是引用的方式。在 Java 程式碼中我們維護一個記憶體物件的引用變數,通過這個引用變數的值,我們可以訪問到對應的記憶體地址中的記憶體物件空間。在 Java 程式中,這個引用變數本身既可以存放堆記憶體中,又可以放在程式碼棧的記憶體中(與基本資料型別相同)。 GC 執行緒會從程式碼棧中的引用變數開始跟蹤,從而判定哪些記憶體是正在使用的。如果 GC 執行緒通過這種方式,無法跟蹤到某一塊堆記憶體,那麼 GC 就認為這塊記憶體將不再使用了(因為程式碼中已經無法訪問這塊記憶體了)。

通過這種有向圖的記憶體管理方式,當一個記憶體物件失去了所有的引用之後,GC 就可以將其回收。反過來說,如果這個物件還存在引用,那麼它將不會被 GC 回收,哪怕是 Java 虛擬機器丟擲 OutOfMemoryError 。

2.java記憶體洩漏的根本原因是?

答:記憶體物件明明已經不需要的時候,還仍然保留著這塊記憶體和它的訪問方式(引用)。

3.堆溢位:靜態集合類中儲存物件

java.lang.OutOfMemoryError: Java heap space

4.棧溢位

java.lang.StackOverflowError

5.程式碼棧是什麼?

答:

 Vector v = new Vector(10);
        for (int i = 1; i < 100; i++) {
            Object o = new Object();
            v.add(o);
            o = null;
        }

在這個例子中,程式碼棧中存在Vector 物件的引用 v 和 Object 物件的引用 o 。
在 For 迴圈中,我們不斷的生成新的物件,然後將其新增到 Vector 物件中,之後將 o 引用置空。

6.當 o 引用被置空後,如果發生 GC ,我們建立的 Object 物件是否能夠被 GC 回收呢?


答案是否定的。
因為, GC 在跟蹤程式碼棧中的引用時,
會發現 v 引用,而繼續往下跟蹤,就會發現 v 引用指向的記憶體空間中又存在指向 Object 物件的引用。也就是說盡管 o 引用已經被置空,
但是 Object 物件仍然存在其他的引用,是可以被訪問到的,所以 GC 無法將其釋放掉。
如果在此迴圈之後, Object 物件對程式已經沒有任何作用,
那麼我們就認為此 Java 程式發生了記憶體洩漏。

7.記憶體洩漏在哪個領域比較常見?
答:在移動裝置對於記憶體和 CPU都有較嚴格的限制的情況下, Java 的記憶體溢位會導致程式效率低下、佔用大量不需要的記憶體等問題。這將導致整個機器效能變差,
嚴重的也會引起丟擲 OutOfMemoryError ,導致程式崩潰。

8.如何避免記憶體洩漏?

答:明確引用變數的生命週期,是方法內部的區域性變數,還是類例項變數,與類例項生命週期相同的要宣告為例項變數。

要避免這種情況下的記憶體洩露,要求我們以C/C++ 的記憶體管理思維來管理自己分配的記憶體。第一,是在宣告物件引用之前,明確記憶體物件的有效作用域。在一個函式內有效的記憶體物件,應該宣告為 local 變數,與類例項生命週期相同的要宣告為例項變數……以此類推。第二,在記憶體物件不再需要時,記得手動將其引用置空。