2012-04-25 73 views
2

我構建了一個示例程序,演示了java中的內存泄漏。是內存泄漏嗎?

public class MemoryLeakTest { 
    static int depth = 0; 
    int number=0; 
    MemoryLeakTest mobj; 

    MemoryLeakTest(){ 
     number = depth; 
     if(depth < 6500){ 
      depth++; 
      mobj = new MemoryLeakTest(); 
     } 
    } 

    protected void finalize(){ 
     System.out.println(number + " released."); 
    } 

    public static void main(String[] args) { 
     try{ 
      System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()); 
      System.out.println("Free Memory in starting "+ Runtime.getRuntime().freeMemory()); 
      MemoryLeakTest testObj = new MemoryLeakTest(); 
      System.out.println("Free Memory in end "+ Runtime.getRuntime().freeMemory()); 
      System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()); 
     } 
     catch(Exception exp){} 
     finally{ 
      System.out.println("Free Memory"+ Runtime.getRuntime().freeMemory()); 
      System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()); 
     } 
    } 

} 

我通過在if(depth < N)中更改N的值來運行它。這是結果;

當深度是1000

INIT = 16777216使用(16384K)= 288808(282K)致力於= 16252928(15872K)最大= 259522560(253440K) 可用內存在端15964120開始15964120 可用內存使用的初始值= 16777216(16384K)= 288808(282K)= 16252928(15872K)max =使用= 12877816(16384K)= 288808 259522560(253440K)

當深度是1500

INIT = 16777216使用(16384K)= 288808(282K)致力於= 16252928(15872K)最大= 259522560(253440K) 可用內存在開始15964120 在端可用內存15964120 INIT = 16777216 (16384K)使用的= 37470K(370K)= 16252928(15872K)最大= 259522560(253440K)

深度爲6000時

INIT =使用16777216(16384K)= 288808(282K)致力於= 16252928(15872K)最大= 259522560(253440K) 可用內存在開始15964120 在端可用內存15692784 INIT = 16777216使用(16384K)= 560144(547K )致力於= 16252928(15872K)最大= 259522560(253440K) 可用內存15692784 INIT = 16777216使用(16384K)= 560144(547K)致力於= 16252928(15872K)最大= 259522560(253440K)

當深度是6500(線程「main」java.lang.StackOverflowError中的異常)

init = 16777216(16384K)used = 288808(282K )提交= 16252928(15872K)max = 259522560(253440K) 啓動時的空閒內存15964120 空閒內存最後15676656 init = 16777216(16384K)used = 576272(562K)committed = 16252928(15872K)max = 259522560(253440K)

我的問題是;

  1. 它沒有調用finalize()。它是內存泄漏嗎?
  2. 自由內存不超過N = 1000。但是當N = 1500時,在程序結束時有兩個不同的 值,即282K和370K。 這是爲什麼呢?
  3. 當N = 6500時,JVM產生錯誤。那麼爲什麼最後2 try {}的語句被執行。
+0

** **的觀察,我發現,JVM開始釋放記憶,當我創造大量的對象,如1-2十萬。這意味着它不是內存泄漏,但GC在實際需要時開始釋放內存。而且,它首先釋放堆上的對象頂部,就像它先釋放對象9000然後8999然後釋放8998.有趣的是。 – 2012-04-26 00:53:44

+0

觀察2:即使在運行GC之後,getHeapMemoryUsage也會給出更高的使用空間。 – 2012-04-26 00:54:51

+0

你可以看到http://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java:對於Java中的內存泄漏非常有用! – Val 2012-06-16 13:02:26

回答

1

你的程序不會「泄漏」,因爲Java會照顧任何「懸空」的東西。這是垃圾收集語言的好處。

但是你有什麼是StackOverFlow錯誤。基本上,堆棧(這是你所在的函數鏈,以及它的深度)比堆小得多。堆是「或多或少」主存儲器的大小。每個線程的堆棧要小得多。基本上你通過做你的「深度」的事情達到了極限。

如果你想測試「泄漏」(或者,你不會有任何最終的想法)嘗試更多的東西是這樣的:

public class MemoryLeakTest { 
    int number=0; 
    public MemoryLeakTest mobj; 

    MemoryLeakTest(int num){ 
     number = num; 
    } 

    protected void finalize(){ 
     System.out.println(number + " released."); 
    } 

    public static void main(String[] args) { 
     try{ 
      System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()); 
      System.out.println("Free Memory in starting "+ Runtime.getRuntime().freeMemory()); 
      MemoryLeakTest first = new MemoryLeakTest(0); // Keep a reference to one of them 
      MemoryLeakTest current = first; 
      for(int i = 1; i < Int.Parse(args[0]); i++) // forgive me, Java's been a while. This may be C#. But parse the first arg for your number of objects 
      { 
       current.mobj = new MemoryLeakTest(i); 
       current = current.mobj; 
      } 
      System.out.println("Free Memory in end "+ Runtime.getRuntime().freeMemory()); 
      System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()); 
     } 
     catch(Exception exp){} 
     finally{ 
      System.out.println("Free Memory"+ Runtime.getRuntime().freeMemory()); 
      System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()); 
     } 
    } 

} 

這會給你所有對象的「產業鏈」直到first超出範圍。

+0

你的for循環絕對不是java,但我沒有得到你所做的足夠好的編輯0_0 – Lucas 2012-04-25 05:34:19

+0

我所做的只是使用程序的第一個參數來確定要創建多少個對象,以及在循環中這樣做。 – 2012-04-25 13:53:14

+0

+1第二節程序。但是,當我用自己的方式修改程序時,我面臨同樣的問題(不包括例外部分)。 – 2012-04-26 00:43:16

0

這不是內存泄漏的證據。該方案投擲StackOverflowError而不是OutOfMemoryError。實際上,這是因爲遞歸調用自己的構造函數,並且當遞歸調用的數量超過了一些很大的數字(介於6,000和6,500之間)時,用盡空間堆棧

It is not calling finalize(). Is it memory leak?

不可以。因爲GC沒有運行,finalize()方法很可能不會被調用。並沒有運行,因爲你沒有填滿堆。即使這不是真正的解釋,也不能保證finalize()方法會被調用。您唯一的絕對保證是finalize()在之前將被稱爲該對象的內存被JVM重用。

There is not change in free memory up to N=1000. But when N=1500 there is 2 different values for used memory at the end of the program ie 282K and 370K. Why does it so?

我不知道爲什麼會發生這種情況,但我不認爲這說明有什麼重大意義。 (有一個JVM,可以是事物如內存分配和使用模式不確定性的來源引擎蓋下所發生的各種事情。)

When N=6500, JVM generates error. So why last 2 statements of try{} are executed.

finally總是執行,除非聲明JVM突然終止。當StackOverflowError被引發時,它像其他異常一樣傳播,並且可以從(在某些情況下)被捕獲和恢復。

1

It is not calling finalize(). Is it memory leak?

的Finalize不能保證被調用,當垃圾收集器收集給定對象的被調用,但不能保證對象的執行結束之前被收集。

There is not change in free memory up to N=1000. But when N=1500 there is 2 different values >for used memory at the end of the program ie 282K and 370K. Why does it so?

我認爲這取決於垃圾回收器的執行情況以及執行時刻。

When N=6500, JVM generates error. So why last 2 statements of try{} are executed.

這是因爲你沒有趕上從Error因爲StackOverflowError繼承不同之處在於不除繼承分支的一部分,而是爲Exception兄弟,反正你在catch無碼,最後您的嘗試的兩種方法不會被執行,因爲異常已被拋出。

總之,您沒有產生內存泄漏,當您有引用的對象可以直接或間接地從執行流到達某處時發生內存泄漏,例如您將對象存儲在集合中你可以達到,或單身。

垃圾回收器本身足夠聰明,可以釋放從程序中無法訪問的對象圖。

希望我能說清楚。

+0

它令人困惑「無論如何,你在catch中沒有代碼,你的嘗試的最後兩個方法沒有被執行,因爲拋出了異常。」我說最後2條語句被執行。 – 2012-04-26 00:37:57

+0

另外,我已經嘗試手動調用gc()。仍然沒有調用finalize(),並且在程序結束時使用空間很高。 – 2012-04-26 00:41:53

1

It is not calling finalize(). Is it memory leak?

沒有沒有內存泄漏,你總是可以訪問參考存到自己testObj對象 ,這就是爲什麼finalize永遠不會在你的應用程序調用的原因。

您在應用程序中所做的一切就是創建一個巨大的對象圖。

Here你可以找到一個解釋如何在java中創建一個真正的內存泄漏。

+0

'testObj'具有實時參考。但是關於分配給'mobj'的第一個'N-1'實例呢?它們不被任何對象引用。 – 2012-04-26 00:40:20

1

已經大部分的答案解釋了StackOverflowError和內存泄漏之間的區別。

  • 自由內存不超過N = 1000。但是,當N = 1500時,在程序結束時有兩個不同的已用內存值,即282K和370K。爲什麼這樣?

  • 這是因爲每次創建新對象和以前obj變得無法訪問(沒有引用,覆蓋引用),因此可以在需要時釋放。

到目前爲止最簡單的例子,使jvm用完內存(不泄漏)。

public class PrintSeries { 

private static String COMMA = ","; 
private StringBuilder buildStream;// = new StringBuilder(); 

public static void main(String[] args) { 
    System.out.println(new PrintSeries().convert(10)); 
    System.out.println(new PrintSeries().convert(1000000000)); 
} 

private String convert(int n) { 
    buildStream = new StringBuilder(); 

    while (n > 1) { 
     buildStream.append(n-- + COMMA); 
    } 
    buildStream.append(n); 
    return buildStream.toString(); 
    } 
} 
  • 輸出

    10,9,8,7,6,5,4,3,2,1 異常線程 「main」 java.lang.OutOfMemoryError:Java堆空間 在java.util.Arrays.copyOf(Arrays.java:2882) 在java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100) 在java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390) 在java.lang.StringBuilder.append(StringBuilder.java:119) at com.cctest.algotest.st ring.PrintSeries.convert(PrintSeries.java:17) 在com.cctest.algotest.string.PrintSeries.main(PrintSeries.java:10)