繁体   English   中英

是内存泄漏吗?

[英]Is it memory leak?

我构建了一个示例程序来演示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 init =使用的16777216(16384K)=已使用的288808(282K)= 16252928( 15872K)最大= 259522560(253440K)可用内存15964120初始化= 16777216(16384K)已使用= 288808(282K)已提交= 16252928(15872K)最大= 259522560(253440K)

当深度为1500时

init =使用的16777216(16384K)=已提交的288808(282K)= 16252928(15872K)最大= 259522560(253440K)开始的可用内存15964120结束时的可用内存15964120 init =使用的16777216(16384K)=已使用的288808(282K)= 16252928( 15872K)最大= 259522560(253440K)可用内存15873528初始化= 16777216(16384K)已使用= 379400(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初始化= 16777216(16384K)已使用= 560144(547K)已提交= 16252928(15872K)最大= 259522560(253440K)

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

init =使用的16777216(16384K)=已提交的288808(282K)= 16252928(15872K)最大= 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会生成错误。 那么为什么要执行try {}的最后2条语句。

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

Finalize不能保证被调用,当垃圾回收器收集给定的对象但是在执行结束之前不能保证对象被收集时调用Finalize。

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

我认为这取决于垃圾收集器的执行及其执行的时间。

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

这是因为您没有捕获异常,因为StackOverflowError是从Error继承的,而Error不是Exception继承分支的一部分,而是Exception的兄弟,无论如何您都没有代码,尝试的最后两个方法不是正在执行,因为已引发异常。

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

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

希望我能说清楚。

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

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

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

在这里,您可以找到有关如何在Java中创建实际内存泄漏的说明。

您的程序不会“泄漏”,因为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超出范围。

大多数答案已经解释了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堆空间com.cctest.algotest上的java.lang.StringBuilder.append(StringBuilder.java:119)上的java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390)上的java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100) .string.PrintSeries.convert(PrintSeries.java:17)位于com.cctest.algotest.string.PrintSeries.main(PrintSeries.java:10)

这不是内存泄漏的证据。 程序抛出StackOverflowError而不是OutOfMemoryError 实际上,这是因为构造函数正在递归地调用自身,并且当递归调用的数目超过某个较大的数目(6,000到6,500之间)时,您将耗尽堆栈空间。

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

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

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

我不确定为什么会发生这种情况,但是我认为这并不意味着任何重大意义。 (JVM中发生的各种事情可能是内存分配和使用模式之类的不确定因素。)

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM