java - 可能的尝试/捕获和内存管理问题?

标签 java memory-leaks garbage-collection try-catch

我有一个大型Java应用程序,它使用actionPerformed(下面的示例代码)内的try / catch处理大量数据文件。当我在循环中获取约1000个文件时,它会耗尽内存。

每个文件加载合法地占用大约1MB的存储空间,但是我仔细查看过,没有看到该存储空间被挂到的任何地方。每个文件加载都在做相同的事情(即分配相同的变量),因此应该重新使用而不是累加。

我尝试在循环中插入一个显式的gc调用,该调用(根据visualvm)仅能成功消除内存使用量的峰值(请参见下图)。

奇怪的是内存使用的行为:如所附图像所示,使用情况在加载循环工作时攀升,在try内停留在平稳状态,但是try外的gc导致回收所有内存(高原尽头的悬崖)。

关于try / catch是否有影响gc行为的内容?有关在我的代码中检查内容的任何提示,以查找可能引起的泄漏?

我花了很多时间使用各种内存/堆管理工具以及跟踪代码,这确实让我感到困惑。如果这是我的代码中真正的内存泄漏,为什么最终的gc会清除所有内容?

非常感谢您的任何建议/想法。

if (message == MenuCommands.TRYLOADINGFILES){
try {
    File dir = new File(<directory with 1015 files in it>);
    File [] cskFiles = dir.listFiles(ioUtilities.cskFileFilter);
    for (int i=0; i<cskFiles.length; i++){
            loadDrawingFromFile(cskFiles[i], true);
            if (i % 10 == 0) System.gc();
    }
    DebugUtilities.pauseForOK("pausing inside try");
}
catch (Exception e1){
    e1.printStackTrace();
}
DebugUtilities.pauseForOK("pausing outside try");
System.gc();
DebugUtilities.pauseForOK("pausing after gc, outside try");
}


哪里

public static pauseForOK(String msg){
JOptionPane.showMessageDialog(null, msg, "OK", JOptionPane.INFORMATION_MESSAGE);
}




后续根据彼得的建议,如下。 histo:live在运行时(在pgm启动时,在执行任何操作之前,在读取所有文件之后(当visualvm报告正在使用的存储GB时),在最终gc之后,当visualvm表示已恢复为最初的stg使用时)都几乎没有变化)。从启动到运行前四个类别大约增加一倍,Char stg的数量大约增加一个文件处理的预期数量,但其他没有太大变化。

据它看来,似乎没有任何东西在坚持。这是文件加载循环完成之后(在try之外的最终gc之前)之后的histo的前30行左右。

 num     #instances         #bytes  class name
----------------------------------------------
   1:         67824        9242064  <methodKlass>
   2:         67824        9199704  <constMethodKlass>
   3:          6307        7517424  <constantPoolKlass>
   4:          6307        6106760  <instanceKlassKlass>
   5:         46924        5861896  [C
   6:          5618        4751200  <constantPoolCacheKlass>
   7:         10590        3944304  [S
   8:         19427        3672480  [I
   9:         15280        1617096  [B
  10:         33996        1584808  [Ljava.lang.Object;
  11:          2975        1487144  <methodDataKlass>
  12:         40028        1280896  java.util.Hashtable$Entry
  13:         45791        1098984  java.lang.String
  14:         31079         994528  java.util.HashMap$Entry
  15:         10580         973472  [Ljava.util.HashMap$Entry;
  16:          6750         817344  java.lang.Class
  17:         10427         583912  java.util.HashMap
  18:          1521         523224  javax.swing.JPanel
  19:         10008         516344  [[I
  20:          8291         457176  [Ljava.security.ProtectionDomain;
  21:          4022         431800  [Ljava.util.Hashtable$Entry;
  22:           774         377712  com.sun.java.swing.plaf.windows.WindowsScrollBarUI$WindowsArrowButton
  23:           689         369704  [J
  24:         13931         334344  java.util.ArrayList
  25:          7625         305000  java.util.WeakHashMap$Entry
  26:          8611         275552  java.lang.ref.WeakReference
  27:          8501         272032  java.security.AccessControlContext
  28:         16144         258304  javax.swing.event.EventListenerList
  29:          6141         245640  com.sun.tools.visualvm.attach.HeapHistogramImpl$ClassInfoImpl
  30:           426         245376  <objArrayKlassKlass>
  31:          3937         220472  java.util.Hashtable
  32:         13395         214320  java.lang.Object
  33:          2267         199496  javax.swing.text.html.InlineView


无论运行在什么时候,它基本都显示了同一件事。即使没有:live参数,结果也基本相同。但是,如果程序在足够的文件上运行,则肯定会耗尽内存。

另一件事:我使用visualvm的Memory Sampling拍摄了两个快照,一个在pgm starup上拍摄,另一个在内存使用的稳定期拍摄;增量显示了存储使用的预期增长,其中包括与处理的文件数完全相同的某些结构的数量增加。每次文件处理都会创建这些结构之一,就好像所有中间存储都在尝试期间都保留着一样,但是可以在以后清除。

这是怎么回事?

++++++++++++

美国东部时间周日22:00更新

感谢@Peter Lowrey,@ Vampire和其他人的建议。尝试了所有这些想法,但没有任何效果。尝试设置-XX:NewSize = 1GB和-XX:NewRatio = 3,但没有帮助。

try / catch是原始代码的保留,在示例中(我迟来了)是无关紧要的。摆脱它完全不会改变任何事情。只是简单的for循环加载文件会导致相同的内存增长模式,然后是删除
完成最终gc时,将其设置为初始值。

遵循@Vampire的建议,我尝试了此变体(内嵌负载,而不是块内加载):

loadDrawingFromFile(thecskFile, true);
loadDrawingFromFile(thecskFile, true);
... 20 times
DebugUtilities.pauseForOK("after 20 loads, before gc");
System.gc();
DebugUtilities.pauseForOK("after gc outside try");


与整个示例中一样,这20个文件加载在Used Heap空间中所产生的增长量成比例地相同(大约400MB),然后在上面的System.gc()之后,所使用的堆空间立即下降到程序初始化级别,就像之前一样。

发生这种情况时,我尝试了一种更基本的方法

loadDrawingFromFile(thecskFile, true);
DebugUtilities.pauseForOK("after load ");
System.gc();
.. repeated 20 times


从某种意义上说,完成这项工作的意义是,即使加载20个文件,内存使用量也永远不会达到50 MB。

因此,这似乎与线程和线程中断有关。这使我不得不提一个事实:这是一个运行在以以下内容开头的GUI上的应用程序:

SwingUtilities.invokeLater(new Runnable() {
   public void run() { ... }
}


我对线程和Swing实用工具不那么熟悉,所以这也许是天真的错误,但这似乎可以归结为一个事实,即在ShowMessageDialog之前,gc不会触摸很多非活动对象。打断某事。

欢迎其他建议。

最佳答案

我怀疑您没有内存泄漏。取而代之的是您过早地推广大对象。

如果您要创建大型对象,例如byte [],它们直接进入租用空间。这些仅在主要或完整集合中清除。最有可能的是,您仅触发次要集合,而不会触发大型对象,直到触发完整集合为止。

关于java - 可能的尝试/捕获和内存管理问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23875078/

相关文章:

java - App Engine 柔性环境是否支持 Quartz?

java - 空格而不是前导 0 Date Java

C# 树节点移除和内存管理

c# - 终结器和 IDisposable

java - Java 中正在运行的应用程序中的后台进程

java - 什么时候在 Java 中进行类型检查

memory-leaks - 在Lua中,我应该在不使用时将所有表元素分配给nil吗?

c++ - FreeImage_Load 内存泄漏

python - 在循环中调用 openopt SNLE 时出现“内存泄漏”

处理类对象的 MATLAB 弱引用