【问题标题】:Java 1.6 memory leaks: HashMap and ArrayListJava 1.6 内存泄漏:HashMap 和 ArrayList
【发布时间】:2011-12-02 20:06:42
【问题描述】:

编辑: 没有内存泄漏,只是对 Netbeans Profiler 告诉我的内容有误解。 Live Profiling Results -> Allocated Objects 是指在程序生命周期内分配的 TOTAL,而不是当前内存中的总数。为此,请获取堆转储。很抱歉造成混乱,但也许其他人会发现这一点,它会为他们解决这个问题。


我正在使用 NetBeans 7.0.1 分析器来解决我的应用程序中的内存增长问题,并且发现了两个主要问题。我已经成功创建了测试应用程序,并在分析器中看到了相同的结果,如下所示。我无法在其他任何地方找到对这些的参考。

案例 1:循环遍历 HashMap 的条目集导致对象 java.util.HashMap&EntryIterator 增长。除了找出使用不同容器的解决方案之外,这里还有什么可做的吗?每次在 while 循环中遍历集合对我们的应用程序来说都是必要的。

public static void main(String[] args) throws InterruptedException {
    boolean run = true;
    HashMap<Integer, Integer> map = new HashMap<Integer, Integer> ();
    map.put(1, 2);map.put(3, 4);map.put(5, 6);map.put(7, 8);
    while(true){
        for (Entry<Integer, Integer> entry : map.entrySet()) {
        Integer i = entry.getValue();
        }

        //app specific code here

        Thread.sleep(50);
    }
}

案例 2:在循环中创建空的 ArrayList 会导致 java.lang.Object[] 的增长。我希望 ArrayLists 在 while 循环结束时有资格进行 GC。我的假设是错误的吗?还是有别的什么在起作用?

public static void main(String[] args) throws InterruptedException {
    boolean run = true;
    while(run){
        ArrayList<Integer> a1 = new ArrayList<Integer>();
        ArrayList<Integer> a2 = new ArrayList<Integer>();

        // app specific code here

        Thread.sleep(50);
    }
}

【问题讨论】:

  • 你的假设是这些对象在下一个GC周期没有被回收吗?对象在各处创建。问题在于引用它们的那些。
  • 在需要更多内存之前,可能不会回收内存。你确定垃圾收集器已经运行了吗?
  • 问题在于我解释 Netbeans 分析器结果的方式。我用“分配的对象”来表示当前分配的内容,但它实际上是指在程序生命周期中创建的对象数量。我知道这看起来不对。很抱歉给大家带来了困惑,感谢您的回复。
  • 你为什么不自己回答这个问题呢?

标签: java collections memory-leaks garbage-collection


【解决方案1】:

我认为这两个代码示例都没有泄漏内存。您必须确保区分“堆在垃圾收集器运行之前一直增长”和“堆永远增长”。如果您删除 Thread.sleep 调用并永远运行任何一个应用程序,它不应该崩溃,并且 JVM 的堆使用量应该随着垃圾收集器的运行而上升和下降。您可以尝试在每个循环期间运行“System.gc()”并查看您的分析器是否仍然显示泄漏。

【讨论】:

  • 感谢您的快速回复。我添加了 System.gc() 并看到了相同的结果。但需要明确的是,我在 Live Profiling Results、Allocated Objects 列表中看到了这些。当我查看堆转储中的类时,我看不到它们。我认为这可能是由于我自己对分析器告诉我的内容有误解。如果我现在理解正确,我正在查看自程序启动以来的总分配,而不是当前分配的内容。对吗?
【解决方案2】:

只要没有其他代码引用它们,您的第二个示例中的 ArrayList 就符合 GC 条件。此外,您无法控制垃圾回收的执行方式和时间。你知道你用的是什么垃圾收集器吗?您是否使用任何特定的 vm 标志运行程序?在您调用 System.gc 以强制进行垃圾回收后,您是否遇到过奇怪的行为?您的应用特定代码是否引用了您的数组列表?

【讨论】:

    【解决方案3】:

    在您的两个示例中,方法都没有完成,因此即使 GC 运行它也无法声明这些对象。

    即使您的对象是在循环外初始化的,那么在某些编译器上,字节码也会与在循环内创建的相同,但差别不大。

    此外,您无法保证 GC 是否会运行并且您的对象是否会被回收。

    编辑:

    SO上的类似帖子

    【讨论】:

    • 为什么方法需要完成?一旦超出范围(循环结束),他们是否没有资格获得 GC?
    【解决方案4】:

    虽然我找不到任何令人不安的东西,但我只是尝试了您的两个代码 sn-ps。我删除了sleeps 以加快测试速度。垃圾收集器每秒疯狂地工作几次,基本上清理了整个年轻代(尝试使用-XX:+PrintGCDetails -XX:+PrintGCTimeStamps):

    [GC [PSYoungGen: 104272K->0K(99712K)] 105235K->963K(131968K), 0.0008090 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
    

    代码运行了几分钟,CPU 使用率非常高,但内存使用率稳定。两个代码示例中肯定没有内存泄漏

    您可能观察到的是堆的增量增长(JVM 中的年轻一代基本上是一个堆栈,在彼此之上分配新对象,而不是为垃圾而烦恼)。当堆栈耗尽时,垃圾收集运行。

    【讨论】:

      【解决方案5】:

      您所说的很多内容都取决于以下几行:

      // app specific code here
      

      您在此处所做的操作会对垃圾收集产生很大影响。对于您发布的代码,这将导致内存泄漏。比如每次通过这个循环:

      public static void main(String[] args) throws InterruptedException {
          boolean run = true;
          while(run){
              ArrayList<Integer> a1 = new ArrayList<Integer>();
              ArrayList<Integer> a2 = new ArrayList<Integer>();
      
              // app specific code here
      
              Thread.sleep(50);
          }
      }
      

      分配的对象可用于垃圾回收。内存可能会继续增长,直到达到分配的限制,但之后对象将被垃圾回收。

      在 netbeans 之外运行您的代码,然后在其上运行 jvisualvm。看看到底发生了什么。

      【讨论】:

        【解决方案6】:

        我已经运行了上面的代码,我可以确认这不是内存泄漏。因为当我运行 GC 时,所有 ArrayList 对象都被回收了。假设它注释的应用程序代码没有增加问题

        问候, 拉丽莎

        【讨论】:

          猜你喜欢
          • 2013-11-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-05
          • 2015-12-21
          • 2016-09-29
          相关资源
          最近更新 更多