Java 可能的重试/捕获和内存管理问题?

Java 可能的重试/捕获和内存管理问题?,java,memory-leaks,garbage-collection,try-catch,Java,Memory Leaks,Garbage Collection,Try Catch,我有一个处理大量数据文件的大型Java应用程序,在actionPerformed中使用try/catch(下面的示例代码)。当我在循环中得到大约1000个文件时,它的内存就用完了 每个文件加载都会占用大约1MB的存储空间,但我仔细查看了一下,没有发现任何地方会占用该存储空间。每个文件加载都在做相同的事情(即分配相同的变量),因此应该重复使用,而不是累积 我尝试将显式gc调用插入到循环中,这(根据visualvm)只成功地消除了内存使用中的峰值(见下图) 奇怪的是内存使用的行为:正如所附的图片所表

我有一个处理大量数据文件的大型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");
}

根据Peter的建议采取后续行动,如下所示。histo:无论何时运行(在pgm启动时,在采取任何操作之前,在读取所有文件之后(当visualvm报告正在使用GB的存储空间时),在最终gc之后,当visualvm说它返回到初始stg使用时),live都几乎不显示任何更改。从启动到运行前四个类别大约是原来的两倍,Char stg的数量增加了大约一个文件处理的预期数量,但其他方面没有太多变化

根据它,看起来没有什么东西粘在周围。下面是文件加载循环完成后(在try之外的最终gc之前)的大约30行历史记录

20个文件加载按比例产生了与完整示例中相同的已用堆空间增长量(约400MB),然后在上面的System.gc()之后,所用堆空间立即下降到程序初始化级别,与之前一样

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

loadDrawingFromFile(thecskFile, true);
DebugUtilities.pauseForOK("after load ");
System.gc();
.. repeated 20 times
结果证明这是可行的,因为即使加载20个文件,内存使用量也不会达到50MB

所以这似乎与线程和线程中断有关。这让我又提到了一个事实:这是一个运行在GUI上的应用程序,该GUI以:

SwingUtilities.invokeLater(new Runnable() {
   public void run() { ... }
}
我对线程和Swing实用程序不太熟悉,因此这可能是某种形式的天真错误,但这似乎归结为这样一个事实:在ShowMessageDialog中断某些内容之前,gc不会触及许多非活动对象


欢迎提供更多建议。

我怀疑您没有内存泄漏。相反,您会过早地升级大型对象


如果要创建大型对象,例如字节[],则这些对象将直接在永久空间中移动。这些仅在主要或完整集合中进行清理。最有可能的情况是,您只触发了较小的集合,而在触发完整集合之前,大型对象不会被释放。

我想Peter是对的,但如果他不是对的:您可以通过不关闭
loadDrawingFromFile
中的流来耗尽文件描述符。IIRC它也通过OOM表现出来,而你可以有大量的空闲内存。我想这不是您的情况,因为异常消息应该清楚地说明这一点。

不要将其放在try-catch中。它可能会干扰垃圾收集。或者将try-catch放在for循环中…请将代码发布到
loadDrawingFromFile
中好吗?可能有一些有用的东西需要loadDrawingFromFile的内容。保留哪些对象?试试`jmap-histo:live{pid}head-33`@user1359010您只能使用System.gc()触发一个完整的集合;我会考虑回收缓冲区或字节[],这可能不是问题。不能触发任何类型的垃圾回收。gc()只是善意地要求VM进行垃圾收集,但不能保证System.gc()会有任何效果。你试过我的建议了吗?@Vampire虽然没有保证,但实际上,OpenJDK和HotSPot JVM在默认情况下每次都会进行完整的收集。这可以在命令行中更改。@Peter Lawrey:尝试设置-XX:NewSize=1GB和-XX:NewRatio=3,但没有帮助。请参见上面的“更新为原始版本”。你还有什么建议吗?Tnx。@user1359010,当你说“这没用”时“我不相信一开始就有问题。您的GC只在需要的时候运行,它是否需要运行还不清楚。@Maartinus:谢谢您的建议,但是使用visualvm,我可以看到内存从第一次加载文件时就在无情地增长。所以这并不是说我用完了描述符;我最终耗尽了内存,并且随着使用的堆大小的增长,我可以看到内存即将耗尽。@user1359010然后我猜,我的第一个猜测是对的。:)
loadDrawingFromFile(thecskFile, true);
loadDrawingFromFile(thecskFile, true);
... 20 times
DebugUtilities.pauseForOK("after 20 loads, before gc");
System.gc();
DebugUtilities.pauseForOK("after gc outside try");
loadDrawingFromFile(thecskFile, true);
DebugUtilities.pauseForOK("after load ");
System.gc();
.. repeated 20 times
SwingUtilities.invokeLater(new Runnable() {
   public void run() { ... }
}