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());
        }
    }

}

我通过更改 N in 的值来运行它if(depth < N)。这是结果;

当深度为 1000 时

初始化 = 16777216(16384K) 已使用 = 288808(282K) 已提交 = 16252928(15872K) 最大值 = 259522560(253440K) 开始时的可用内存 15964120 结束时的可用内存 15964120 初始化 = 16777216(16384K) 已使用 = 828 (1628829222) 15872K) 最大值 = 259522560(253440K) 可用内存 15964120 初始化 = 16777216(16384K) 已使用 = 288808(282K) 已提交 = 16252928(15872K) 最大值 = 259522560(253440K)

当深度为 1500 时

初始化 = 16777216(16384K) 已使用 = 288808(282K) 已提交 = 16252928(15872K) 最大值 = 259522560(253440K) 开始时的可用内存 15964120 结束时的可用内存 15964120 初始化 = 16777216(16384K) 已使用 = 828 (1628829222) 15872K) 最大值 = 259522560(253440K) 可用内存 15873528 初始化 = 16777216(16384K) 已使用 = 379400(370K) 已提交 = 16252928(15872K) 最大值 = 259522560(253440K)

当深度为 6000

初始化 = 16777216(16384K) 已使用 = 288808(282K) 已提交 = 16252928(15872K) 最大值 = 259522560(253440K) 开始时的可用内存 15964120 结束时的可用内存 15692784 初始化 = 16777216(16777216(16384K) 已使用 = 82145947 已提交 = 82145947 15872K) 最大值 = 259522560(253440K) 可用内存 15692784 初始化 = 16777216(16384K) 已使用 = 560144(547K) 已提交 = 16252928(15872K) 最大值 = 259522560(253440K)

当深度为 6500 时(线程“main”java.lang.StackOverflowError 中的异常)

初始化 = 16777216(16384K) 已使用 = 288808(282K) 已提交 = 16252928(15872K) 最大值 = 259522560(253440K) 开始时的可用内存 15964120 结束时的可用内存 15676656 初始化 = 16777216(16384K) 已使用 = 826(252562) (=576) 15872K) 最大值 = 259522560(253440K)

我的问题是;

  1. 它没有调用 finalize()。是内存泄漏吗?
  2. 在 N=1000 之前,可用内存没有变化。但是当 N=1500 时,程序末尾的已用内存有 2 个不同的值,即 282K 和 370K。为什么会这样?
  3. 当 N=6500 时,JVM 产生错误。那么为什么要执行 try{} 的最后两条语句。
4

5 回答 5

1

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

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

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

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

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

这是因为你没有捕捉到异常,因为从StackOverflowError那里继承Error不是异常继承分支的一部分,而是 的兄弟Exception,无论如何你在 catch 中没有代码,你的最后两个方法没有被执行,因为异常已被抛出。

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

垃圾收集器本身足够聪明,可以释放程序根本无法访问的对象图。

希望我能说清楚。

于 2012-04-25T03:40:10.267 回答
1

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

不,没有内存泄漏,您始终保持对对象的可访问引用,这就是永远不会在您的应用程序中调用testObj的原因。finalize

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

在这里您可以找到如何在 java 中创建真正的内存泄漏的说明。

于 2012-04-25T03:45:29.617 回答
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超出范围。

于 2012-04-25T03:48:31.730 回答
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 线程“主”java.lang.OutOfMemoryError 中的异常:java.util.Arrays.copyOf(Arrays.java:2882) 处的 Java 堆空间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)

于 2012-04-25T03:52:30.370 回答
0

这不是内存泄漏的证据。程序抛出StackOverflowErrorOutOfMemoryError。事实上,构造函数正在递归调用自身,当递归调用的数量超过某个较大的数字(6,000 到 6,500 之间)时,堆栈空间就会用完。

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

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

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

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

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

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

于 2012-04-25T03:33:05.550 回答