是内存泄漏吗?

时间:2012-04-25 03:29:42

标签: java memory-leaks

我构建了一个演示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)used = 288808(282K)committed = 16252928(15872K)max = 259522560(253440K) 启动15964120的免费记忆 自由记忆结束15964120 init = 16777216(16384K)used = 288808(282K)committed = 16252928(15872K)max = 259522560(253440K) 免费记忆15964120 init = 16777216(16384K)used = 288808(282K)committed = 16252928(15872K)max = 259522560(253440K)

深度为1500

init = 16777216(16384K)used = 288808(282K)committed = 16252928(15872K)max = 259522560(253440K) 启动15964120的免费记忆 自由记忆结束15964120 init = 16777216(16384K)used = 288808(282K)committed = 16252928(15872K)max = 259522560(253440K) 免费记忆15873528 init = 16777216(16384K)使用= 379400(370K)已提交= 16252928(15872K)最大= 259522560(253440K)

深度为6000

init = 16777216(16384K)used = 288808(282K)committed = 16252928(15872K)max = 259522560(253440K) 启动15964120的免费记忆 自由记忆在15692784结束 使用的init = 16777216(16384K)= 560144(547K)已提交= 16252928(15872K)max = 259522560(253440K) 免费记忆15692784 init = 16777216(16384K)used = 560144(547K)committed = 16252928(15872K)max = 259522560(253440K)

深度为6500时

(线程“main”java.lang.StackOverflowError中的异常)

init = 16777216(16384K)used = 288808(282K)committed = 16252928(15872K)max = 259522560(253440K) 启动15964120的免费记忆 自由记忆结束于15676656 init = 16777216(16384K)使用= 576272(562K)已提交= 16252928(15872K)最大= 259522560(253440K)

我的问题是;

  1. 它没有调用finalize()。是内存泄漏吗?
  2. N = 1000时,可用内存没有变化。但是当N = 1500时,有2种不同 程序结束时使用的内存值,即282K和370K。 为什么会这样?
  3. 当N = 6500时,JVM会生成错误。那么为什么要持续2 try {}的语句被执行。

5 个答案:

答案 0 :(得分:1)

  

它不是调用finalize()。它是内存泄漏吗?

不保证调用Finalize,当垃圾收集器收集给定对象但在执行结束前不保证收集对象时调用它。

  

N = 1000时,可用内存没有变化。但是当N = 1500时,对于程序结束时的使用存储器,存在2个不同的值,即282K和370K。为什么会这样?

我认为这取决于垃圾收集器的执行情况以及它执行的时刻。

  

当N = 6500时,JVM会生成错误。那么为什么要执行try {}的最后两个语句呢。

这是因为你没有捕获异常,因为StackOverflowError继承自Error,它不属于Exception继承分支,而是Exception的兄弟,无论如何你有在catch中没有代码,你的try的最后两个方法没有被执行,因为抛出了异常。

总而言之,您没有产生内存泄漏,当您在某个时刻引用可以从执行流程中直接或间接访问的对象时,java中会发生内存泄漏,例如,您将对象存储在集合中你可以到达或者单身。

垃圾收集器本身足够智能,可以释放无法从程序中访问的对象图。

希望我能说清楚。

答案 1 :(得分:1)

  

它不是调用finalize()。它是内存泄漏吗?

没有内存泄漏,您始终可以访问testObj对象 这就是为什么永远不会在你的申请中调用finalize的原因。

您在应用程序中所做的只是创建一个巨大的对象图。

Here你可以找到如何在java中创建真正的内存泄漏的解释。

答案 2 :(得分: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超出范围之前,这将为您提供内存中所有对象的“链”。

答案 3 :(得分:1)

大部分答案已经解释了StackOverflowError和内存泄漏之间的区别。

  • N = 1000时,可用内存没有变化。但是当N = 1500时,程序结束时使用的内存有2个不同的值,即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堆空间 at java.util.Arrays.copyOf(Arrays.java:2882) at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100) 在java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390) 在java.lang.StringBuilder.append(StringBuilder.java:119) 在com.cctest.algotest.string.PrintSeries.convert(PrintSeries.java:17) 在com.cctest.algotest.string.PrintSeries.main(PrintSeries.java:10)

答案 4 :(得分:0)

这不是内存泄漏的证据。该计划正在抛出StackOverflowError而不是OutOfMemoryError。事实上,正在进行的是构造函数以递归方式调用自身,并且当递归调用的数量超过一些大数字(在6,000到6,500之间)时,就会耗尽堆栈空间。

  

它不是调用finalize()。它是内存泄漏吗?

没有。由于GC尚未运行,因此很可能未调用finalize()方法。它还没有运行,因为你没有填满堆。即使这不是真正的解释,也无法保证将finalize()方法被调用。唯一绝对的保证是finalize()将在之前被称为,JVM会重用对象的内存。

  

N = 1000时,可用内存没有变化。但是当N = 1500时,程序结束时使用的内存有2个不同的值,即282K和370K。为什么会这样?

我不确定为什么会发生这种情况,但我不认为这表明有什么重大意义。 (在JVM中可能会发生各种各样的事情,这些事情可能是内存分配和使用模式等不确定性的来源。)

  

当N = 6500时,JVM会生成错误。那么为什么要执行try {}的最后两个语句呢。

除非JVM突然终止,否则始终会执行finally中的语句。当抛出StackOverflowError时,它会像任何其他异常一样传播,并且可以被捕获并从中恢复(在某些情况下)。