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 引用置空。問題是當 o 引用被置空后,
如果發生 GC ,
我們創建的 Object 對象是否能夠被 GC 回收呢?
答案是否定的。
因為, GC 在跟蹤代碼棧中的引用時,
會發現 v 引用,而繼續往下跟蹤,就會發現 v 引用指向的內存空間中又存在指向 Object 對象的引用。也就是說盡管 o 引用已經被置空,
但是 Object 對象仍然存在其他的引用,是可以被訪問到的,所以 GC 無法將其釋放掉。
如果在此循環之后, Object 對象對程序已經沒有任何作用,
那么我們就認為此 Java 程序發生了內存泄漏。
7.內存泄漏在哪個領域比較常見?
答:在移動設備對於內存和 CPU都有較嚴格的限制的情況下, Java 的內存溢出會導致程序效率低下、占用大量不需要的內存等問題。這將導致整個機器性能變差,
嚴重的也會引起拋出 OutOfMemoryError ,導致程序崩潰。
8.如何避免內存泄漏?
答:明確引用變量的生命周期,是方法內部的局部變量,還是類實例變量,與類實例生命周期相同的要聲明為實例變量。
要避免這種情況下的內存泄露,要求我們以C/C++ 的內存管理思維來管理自己分配的內存。第一,是在聲明對象引用之前,明確內存對象的有效作用域。在一個函數內有效的內存對象,應該聲明為 local 變量,與類實例生命周期相同的要聲明為實例變量……以此類推。第二,在內存對象不再需要時,記得手動將其引用置空。