2013-03-12 74 views
1

我正在做一个有关Java及其容量允许内存泄漏的实验。我写了这个简单的代码:Java:内存泄漏奇怪与列表

import java.util.ArrayList; 
import java.util.List; 

public class Test { 

public static void main(String[] args) { 

    class Obj { 
     int i; 

     Obj(int i) { 
      this.i = i; 
     } 
    } 

    List<Obj> list; 
    while(true) { 
     list = new ArrayList<Obj>(); 
     for(int i = 0; i < 1000; i++) { 
      Obj o = new Obj(i); 
      list.add(o); 
     } 

     try { 
      Thread.sleep(1); //<-- added to give the gc time to trash the previous iteration 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 
} 

当这被执行(尤其是更多的对象被添加到列表中每个迭代),您可以快速观看使用的内存的增加量。我能够在每次迭代中添加10000个对象的情况下非常快速地达到2 GB。它看起来好像列表(以及利用“链接列表”列表的其他类型的对象,如函数)不喜欢被删除。

当我没有将对象添加到列表中时,内存堆根本没有增加(意味着垃圾处理正在完成它的工作)。我试着将列表重置为空,然后重新声明以及调用clear()方法。似乎没有任何工作。每当我在一个循环中使用List,我的RAM就会呼救。

那么,为什么会发生这种情况呢?为什么垃圾处理不是每次迭代都要摆脱清单,而不是让它们叠加起来?列表界面是否不允许这种用法?我只是没有给予垃圾处理足够的时间摆脱最后的情况?

+1

注意:'Thread.sleep(1)'睡眠1毫秒,而不是1秒。 – 2013-03-12 19:39:24

+0

是的,我知道。我也尝试过更长的持续时间。 – CoderTheTyler 2013-03-12 19:39:48

+2

使用'System.gc()'运行垃圾回收。睡眠不会有伎俩。而且,'list'仍然是从当前函数框架引用的,所以它可能不会被收集。 – kan 2013-03-12 19:40:52

回答

5

你的代码很好。除非你在JVM中遇到了一些晦涩难懂的问题,否则也没有内存泄漏。可能发生的情况是,您已经给了JVM大量的内存来玩,并决定使用该内存。

经过试验你的代码,我可以重现你所看到的唯一方法就是配置一个非常大的年轻一代堆(-Xmn)。 JVM不需要运行垃圾收集器,直到年轻一代已满,所以这个过程最终使用了很多内存。

但是,当年轻一代堆满了时,GC确实会运行并收集所有不可访问的对象。

在下面的截图,右上图是显示堆大小:

VisualVM

正如你所看到的,仅仅是19时47分50秒之前大幅下降。这是年轻一代充满时,当GC运行并收集了所有旧名单的时候。

请注意,如果您正在使用操作系统工具来监视内存使用情况,那么运行垃圾回收器时可能看不到下降。当一些堆对象被释放时,他们以前占用的内存通常不会被释放回操作系统。但是,它可以通过相同的过程重新使用。

如果内存使用率是一个问题,您需要重新审视您提供给JVM的选项。

+0

我稍后在程序和短时间内做类似的过程,但内存永远不会释放。即使我让程序坐在那里,内存也会慢慢堆积起来。 – CoderTheTyler 2013-03-12 19:54:31

+0

也许你对此是正确的。也许这是任务管理器没有正确读取内存使用情况?我试着把它连接起来,这样我就可以停止创建列表,然后重新开始,但内存永远不会放过(根据任务管理器。) – CoderTheTyler 2013-03-12 19:56:59

+0

我也做了同样的事情,并且结束了一个非常类似的图表。我在java 7中在linux上。 – digitaljoel 2013-03-12 19:57:03

0

(一般情况下我只是发表意见,并要求您设定的虚拟机的选择,但我没有足够的代表处点做到这一点)

我只是猜测,但也许你设置-Xms到1GB? 此代码不会泄漏,但取决于您的VM选项,该过程可能仍需要大量内存。

0

代码中没有内存泄漏。你可以100%确定。 但是,这并不意味着即使所有当前活动的对象都比堆小,也不能耗尽内存。

你不能用像你这样的代码来测试垃圾回收。 GC不会每次都调用,并且不会一次清理所有内容。如果你的数据大量刷新的速度很快,你的GC将没有时间踢进去,并且你的代码可能已经不能满足你的需求。

了解内存泄漏是否使用分析工具进行测试,而不是通过监视应用程序内存是否用完也很重要。