1

当我尝试在网格计算机上运行程序时,我遇到了与(打开的文件句柄过多)类似的问题。增加此资源上打开文件总数的操作系统限制的选项不可用。

我试图捕捉并处理异常,但似乎没有捕捉到异常。该异常似乎将自身报告为FileNotFoundException. 抛出异常的地方之一是如下所示的方法:

public static void saveImage(BufferedImage bi, String format, File aFile) {
  try {
    if (bi != null) {
      try {
        //System.out.println("ImageIO.write(BufferedImage,String,File)");
        System.err.println("Not really an error, just a statement to help with debugging");
        ImageIO.write(bi, format, aFile);
      } catch (FileNotFoundException e) {
        System.err.println("Trying to handle " + e.getLocalizedMessage());
        System.err.println("Wait for 2 seconds then trying again to saveImage.");
        //e.printStackTrace(System.err);
        // This can happen because of too many open files.
        // Try waiting for 2 seconds and then repeating...
        try {
          synchronized (bi) {
            bi.wait(2000L);
          }
        } catch (InterruptedException ex) {
          Logger.getLogger(Generic_Visualisation.class.getName()).log(Level.SEVERE, null, ex);
        }
        saveImage(
        bi,
        format,
        aFile);
      } finally {
        // There is nothing to go in here as ImageIO deals with the stream.    
      }
    }
  } catch (IOException e) {
    Generic_Log.logger.log(
    Generic_Log.Generic_DefaultLogLevel, //Level.ALL,
    e.getMessage());
    String methodName = "saveImage(BufferedImage,String,File)";
    System.err.println(e.getMessage());
    System.err.println("Generic_Visualisation." + methodName);
    e.printStackTrace(System.err);
    System.exit(Generic_ErrorAndExceptionHandler.IOException);
  }
}

以下是 System.err 的一个片段,该片段曾在问题发生时报告过:

Not really an error, just a statement to help with debugging   
java.io.FileNotFoundException: /data/scratch/lcg/neiss140/home_cream_292126297/CREAM292126297/genesis/GENESIS_DemographicModel/0_99/0/data/Demographics/0_9999/0_99/39/E02002367/E02002367_Population_Male_End_of_Year_Comparison_2002.PNG (Too many open files) 
  at java.io.RandomAccessFile.open(Native Method)
  at java.io.RandomAccessFile.(RandomAccessFile.java:216) 
  at javax.imageio.stream.FileImageOutputStream.(FileImageOutputStream.java:53) 
  at com.sun.imageio.spi.FileImageOutputStreamSpi.createOutputStreamInstance(FileImageOutputStreamSpi.java:37) 
  at javax.imageio.ImageIO.createImageOutputStream(ImageIO.java:393) 
  at javax.imageio.ImageIO.write(ImageIO.java:1514) 
  at uk.ac.leeds.ccg.andyt.generic.visualisation.Generic_Visualisation.saveImage(Generic_Visualisation.java:90) 
  at uk.ac.leeds.ccg.andyt.generic.visualisation.Generic_Visualisation$ImageSaver.run(Generic_Visualisation.java:210) 
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439) 
  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) 
  at java.util.concurrent.FutureTask.run(FutureTask.java:138) 
  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
  at java.lang.Thread.run(Thread.java:662)

我有一些解决这个问题的想法,但有人知道出了什么问题吗?

(我试图发布这个问题的一个版本作为这个问题的答案但被版主删除了。)

4

1 回答 1

1

首先,如果打开输出流失败,该write方法实际上会抛出一个IIOExceptionnot a ;FileNotFoundException请参阅源代码- 第 1532 行。这解释了为什么您的恢复代码永远不会运行。

其次,你的恢复策略有点可疑。您无法保证使用所有这些文件句柄的任何东西都会在 2 秒内释放它们。事实上,在最坏的情况下,他们可能永远不会被释放。

但最重要的是你把注意力集中在问题的错误部分。与其尝试提​​出恢复机制,不如关注应用程序为什么打开这么多文件描述符的问题。这闻起来像资源泄漏。我建议您在代码库上运行 FindBugs 以查看它是否可以识别泄漏代码。你的代码在任何地方打开一个外部流,它应该close()在一个块中有一个匹配的调用,finally以确保流始终是关闭的;例如

    OutputStream os = new FileOutputStream(...)
    try {
        // do stuff
    } finally {
        os.close();
    }

或者

    // Java 7 form ...
    try (OutputStream os = new FileOutputStream(...)) {
        // do stuff
    }

我正在运行它的资源只有 1024 个文件处理程序,而改变它是另一个问题。该程序是一个模拟程序,它在读入另一批输入数据的同时写出大量输出文件。该工作使用 ExecutorService 进行线程化。该程序在另一台具有更高文件处理程序限制的计算机上运行完成,但我想让它在我受限于文件处理程序较少的资源上工作。

因此,您似乎是在说您需要打开该数量的文件。

令我震惊的是,根本问题在于您的应用程序的体系结构。听起来您只是同时运行了太多模拟任务。我建议您将执行程序的线程池大小减少到比打开文件描述符的最大数量少一些。

问题是您当前的策略可能会导致某种形式的死锁......现有任务在新任务开始运行之前无法取得进展,但新任务在现有任务释放文件描述符之前无法启动。

我认为您需要一种不同的方法来处理输入和输出。要么在内存中缓冲完整的输入和/或输出文件(等等),要么实现某种多路复用器,以便不需要同时打开所有活动文件。

于 2013-01-22T14:57:40.317 回答