有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java可能的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);
}

visualvm memory graph

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

根据它的说法,看起来什么都没有。下面是文件加载循环完成后(在try之外的最终gc之前)的大约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

它基本上显示了相同的东西,无论它在流程的哪个点运行。即使没有现场辩论,结果也基本相同。然而,如果程序运行在足够多的文件上,它肯定会耗尽内存

还有一项:我用visualvm的记忆取样拍摄了两张快照,一张是在pgm启动时拍摄的,另一张是在记忆使用平稳期拍摄的;增量显示了存储使用的预期增加,包括某些结构的数量增加,这与处理的文件数量完全相同。当每个文件处理创建其中一个结构时,就好像所有中间存储都在try内部保留,但可以在之后清除

怎么回事

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

美国东部时间周日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个文件加载所产生的已用堆空间(约400MB)的增长量与完整示例中的增长量成比例,然后是在系统运行之后。如上所述,使用的堆空间会立即下降到程序初始化级别,就像以前一样

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

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触及

欢迎提出更多建议


共 (1) 个答案

  1. # 1 楼答案

    我猜Peter是对的,但如果他不是这样的话:如果不关闭loadDrawingFromFile中的流,文件描述符可能会用完。IIRC它也通过OOM表现出来,而你可以有大量的空闲内存。我想这不是你的情况,因为例外信息应该清楚地说明这一点