2012-04-25 123 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)