Java桌面应用程序的内存分析

Java桌面应用程序的内存分析,java,memory-management,memory-leaks,garbage-collection,profiling,Java,Memory Management,Memory Leaks,Garbage Collection,Profiling,我的应用程序每次加载大约85bm到100mb的数据集。应用程序的内存限制设置为512mb,从理论上讲,这已经足够了 但是,我发现,如果在应用程序的一次运行中,我打开和关闭了数据集5次,那么总内存消耗会稳步增加,直到出现内存不足错误: PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 6882 bguiz 20 0 679m 206m 19m S 30 13.7 0:30.22 java 6882 bguiz 20 0 679m

我的应用程序每次加载大约85bm到100mb的数据集。应用程序的内存限制设置为512mb,从理论上讲,这已经足够了

但是,我发现,如果在应用程序的一次运行中,我打开和关闭了数据集5次,那么总内存消耗会稳步增加,直到出现内存不足错误:

 PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+   COMMAND
6882 bguiz 20 0 679m 206m 19m S 30   13.7 0:30.22 java
6882 bguiz 20 0 679m 259m 19m S 9    17.2 0:55.53 java
6882 bguiz 20 0 679m 301m 19m S 9    20.0 1:20.04 java
6882 bguiz 20 0 679m 357m 19m S 33   23.7 1:44.74 java
6882 bguiz 20 0 679m 395m 19m S 80   26.2 2:10.31 java
记忆从约14%增长到约26%。看起来像是内存泄漏

正在发生的事情是,正在加载的顶级数据用于填充集合,如地图和列表,然后更详细的数据用于创建这些顶级对象的子对象,然后它们又创建子对象

当数据集关闭时,当前应用程序确实尝试通过取消填充各种对象集合,然后显式调用
System.gc()来清除其轨迹


不管怎么说,这就是我得到应用程序时的状态(在我之前的几年中),我被分配了这个任务

我需要做的是找到一种方法,找出卸载数据集后哪些子对象和子对象仍在相互引用,并纠正它们。
显然,这可以手动完成,但会非常乏味,但我觉得通过内存分析来完成这项工作会是一个更好的选择,这是我以前没有做过的

我已经阅读了一些其他的SO问题,这些问题询问了应该使用哪些内存分析工具,我选择使用内置在Netbeans IDE中的工具,因为它似乎有很好的评价,而且我正在Netbeans中工作

以前有没有人从事过类似的Java内存分析任务,事后来看:

  • 你会给我什么具体建议
  • 在解决这个问题时,您发现了哪些有用的技术
  • 您发现哪些资源对解决此问题有用

编辑: 此应用程序是标准桌面应用程序,而不是web应用程序


编辑:实现的解决方案 基本上对我有效的是将Netbeans的profiler与JHAT结合使用。

我发现内置在Netbeans IDE中的探查器在特定的评测点创建内存转储方面做得非常好,然后该工具能够按类过滤和排序,并深入每个实例的引用。一切都很好

然而,它并没有为我提供一种比较两个堆转储的方法。我问了一个问题,看来JHAT(作为JDK的一部分)做得相当好


Thorbjørn Ravn Andersen、Dmitry和Jason Gritman:您的输入非常有用,不幸的是,我只能将1标记为正确答案,而且所有人都从我这里得到+1。

您看到的行为不一定是内存泄漏。调用System.gc()只是向VM提示垃圾收集器应该运行(不一定要运行),只要堆有足够的可用空间,垃圾收集器通常不会运行。因此,看到进程大小的增加并不能证明旧的数据收集不是垃圾收集器真正可以请求的。如果您不确定,我建议您对流程进行堆转储(具体方式取决于您使用的Java VM,但其文档应该会告诉您),并在堆转储上使用分析工具,查看堆中是否有比预期更多的对象实例,以及从何处引用它们(这也可以解释内存泄漏的位置)


作为第一次尝试,我可能会尝试使用Java 1.6.0_14以来提供的新版本运行该程序。在正常情况下,它可能更好地更早地到达可请求的实例,并且还具有能够将不需要的内存返回到操作系统的优势。其他Java垃圾收集器也有这个问题lem指出,在进程退出之前,通常不会返回从操作系统分配的内存。

NetBeans profiler可能是空闲内存中最好的一个。它工作得很好,没有关于使用profiler本身的特殊提示。关于代码,请注意缓存内容(特别是静态内容)的哈希映射。它们通常是内存泄漏的来源。请尝试使用WeakHashMap进行缓存(大致上,它不会创建对缓存值的强引用)。

启动应用程序时是否更改内存分配?例如:

java -Xmx512m -Xms128m foo.Bar

我的理解是,当JVM不能足够快地分配内存时,也会发生内存不足错误。即使它的上限为512m(在上面的示例中),如果JVM不能足够快地分配内存(超过上面最初的128M),可能会发生内存不足错误。如果出现问题,从更高的-Xms值开始可以缓解这种情况。还需要注意的是,Xms和Xmx值是建议,而不是硬性规则。

查找静态集合(映射、集、列表)用作缓存。

我写了另一个问题的答案,这个问题是关于在内存中查找内存泄漏的技术

如果你听从我的建议,像这样的工具可以让你浏览对象的参考图并查看这些对象的深度。这可以帮助你找到仍然保留在数据上的任何对象


我没有使用过Netbeans,因此我无法告诉您它与我使用过的其他探查器的对比情况。如果它看起来不具备该功能,您可以轻松获得JProfiler的试用版,该版本将一直持续到您发现漏洞为止。

使用Java 6 JDK中的jvisualvm连接到您的程序,并查看您的内存在哪里去。

也是分析堆转储的一个很好的独立工具。
您有几个选项可以创建堆转储(例如,在OutOfMemoryExceptions上)。

他说他是