在一次采访中,我被问到如何检测Java中的内存泄漏?

时间:2018-06-15 19:03:15

标签: java memory-management memory-leaks garbage-collection

我不知道如何回答这个问题。相反,我告诉他们我没有在 Web应用程序中看到任何可能导致内存泄漏的实际代码/情况。我还告诉他,如果创建的对象太多而且这些对象超出范围, GC 将负责回收内存。

我的问题是 (1)我的回答是否足够好? (2)您能否在Web和/或非Web环境中给我一个Java内存泄漏的实际示例?

由于

3 个答案:

答案 0 :(得分:2)

好主题!

您需要首先监控Java内存消耗。

最简单的方法是使用每个JVM附带的jstat实用程序。

jstat -gcutil <process_id> <timeout>

它将报告每一代(Young,Eldery和Old)的内存消耗和垃圾收集时间(Young和Full)。

一旦发现Full Garbage Collection执行过于频繁并且花费太多时间,您就可以认为应用程序正在泄漏内存。

然后,您需要使用jmap实用程序创建内存转储:

jmap -dump:live,format=b,file=heap.bin <process_id>

然后,您需要使用Memory Analyzer,Eclipse Memory Analyzer(MAT)分析heap.bin文件。

MAT将分析内存并为您提供有关内存泄漏的可疑信息。

答案 1 :(得分:0)

内存泄漏,在任何情况下,您继续保持已分配的内存,不再需要并且不再打算使用。

考虑以下示例:

public class LeakMemory {
    private static final List<String> LEAK = new ArrayList<>();

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("What is your name? ");
        while (in.hasNext()) {
            name = in.next();
            System.out.println("Hi " + name);
            LEAK.add(name);
        }
    }
}

LEAK列表在每次迭代中都会增长,并且没有办法将其释放,但它从未使用过。这是泄密。

答案 2 :(得分:0)

通过memory leak的正常定义,由于垃圾收集,Java没有它们。但是,如果我们将定义稍微扩展为“程序不再需要的对象,但不是垃圾可收集的”,那么有两种情况我可以想到可能出现这种“泄漏”的地方。

场景1:记录所创建的类的所有对象

这个例子具有人造味道,常常在练习的背景下看到。假设我们想要存储所创建的某个类的每个实例。这可以通过静态集合来实现:

public class RecordMe {
  private static final List<RecordMe> created = new ArrayList<RecordMe>();
  ...
  private final int someValue;

  private RecordMe(final int someValue) {
    this.someValue = someValue;
  }

  public static RecordMe of(final int someValue) {
    RecordMe me = new RecordMe(someValue);
    created.add(me);
    return me;
  }

  public static List<RecordMe> instances() {
    return Collections.unmodifiableList(created);
  }
}

只要创建RecordMe的实例,它就永远不会被垃圾收集,因为它将始终通过静态列表引用。

修复方法是在创建RecordMe的新实例或使用List< WeakReference<RecordMe> >之前检查列表(并不时清理此列表)。

场景2:内部课程泄漏

正如我们所知,非staitc内部类拥有对其创建对象的隐式引用。让我们来看一个极端的例子。

public class MemoryTest {
  // data has a size of a little over 512 MB.
  private final byte[] data = new byte[1024 * 1024 * 512];
  private final Field field;

  public class Field {
  }

  public MemoryTest() {
    this.field = new Field();
  }

  public Field getField() {
    return this.field;
  }

  public static void main(String... args) {
    MemoryTest test = new MemoryTest();
    Field fieldOne = test.getField();
    test = null;
    test = new MemoryTest();
  }
}

如果我们使用java -Xmx800m MemoryTest执行此代码,则会抛出OutOfMemoryException。这种尺寸的例子在现实中是不现实的,但是在较小的尺寸和足够的实例中,这也可能导致问题。举例来说,Java的HashMap - 实现。方法keySet()返回非静态内部类的实例。只要有一个实例保存那些内部类,HashMap就不能被垃圾收集。