1. 程式人生 > >Java記憶體洩漏問題--java既然存在垃圾回收機制,為什麼還存在記憶體洩漏?

Java記憶體洩漏問題--java既然存在垃圾回收機制,為什麼還存在記憶體洩漏?

1.什麼叫記憶體洩漏?

簡單來說就是一個東西放在記憶體裡的時間太長了,當你的程式都跑完了,它還存在那裡。這時它是白白的佔用了你的記憶體,累積起來佔用的記憶體越來越多……最後就會導致JVM報錯:out of memory。他佔用的是我們的實體記憶體。

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

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

3.java既然存在gc執行緒,為什麼還存在記憶體洩漏?

這個問題,我們需要知道 GC 在什麼時候回收記憶體物件,什麼樣的記憶體物件會被 GC 認為是“不再使用”的。

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

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


例子1:
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 程式發生了記憶體洩漏。

例子2:

如果想要看到記憶體溢位,可以按這樣的思路去嘗試一下:定義一個靜態的例項變數(list或其它集合),然後在一個方法裡迴圈往這個靜態變數塞東西,直到這個例項變數撐爆你的jvm記憶體。很快你就能看到out of memory……

import  java.util.ArrayList; import  java.util.List;   public  class
  MemoryTest {      private  static  List list =  new  ArrayList();      private  static  int  count =  0 ;            public  static  void  main(String[] args)  throws  InterruptedException {          System.out.println( "申請前的可用記憶體 = " +getFreeMemory());          while ( true ){               list.add( new  byte [ 1024 * 1024 ]); //用例項變數申請1M記憶體,當方法執行完畢時,這個static的變數是不會被釋放               count++;               if  (count %  100  ==  0 ) {                   System.out.println( "當前list.size()=" +list.size()+ ",可用記憶體 = " +getFreeMemory());                   Thread.sleep( 500 );               }          }      }            public  static  long  getFreeMemory() {            return  Runtime.getRuntime().freeMemory() / ( 1024  1024 );           }   }

所以我們要慎用類變數。