【问题标题】:Too many open files error打开的文件太多错误
【发布时间】:2012-08-27 09:59:08
【问题描述】:

简介

我在网上找到了大量关于打开文件过多异常的信息,但我无法解决这个奇怪的情况。正如我所读到的,当超过操作系统中定义的进程打开的文件描述符的数量时,将引发异常。这些文件的性质是多种多样的。文件可以是套接字、文档等。我已经找到了打开在我的 Java 应用程序中实现的文件的可靠且安全的方法。

该应用程序是一个使用Boilerpipe 算法下载网页的短程序。这样我就得到了那个网站最具代表性的内容。然后,我以适当的格式(TREC 格式)将它写入磁盘。这些网站的 URL 取自我使用 JDBC 连接器 访问的 MySQL 数据库。

所以,我认为异常可以从三个不同的地方抛出:

  • 与数据库的连接
  • 到网站的 HTTP 连接
  • 打开和写入文件

虽然,正如我所说,我认为我使用了正确的方式来打开和写入这些文件。

问题

有数千个 URL 需要处理,一段时间后抛出异常(这也使得调试变得非常困难......)。我不知道这是否重要,但 URL 分为不同的类别,我运行程序的不同实例以加快整个过程。类别不重叠,所以应该没有任何问题。

代码

为了使其更具可读性,我将仅展示我简化的代码的这三个部分:

  1. 数据库访问

    // Connect to database
    Connection dbconn = null;
    
    try {
        String dbUrl = "jdbc:mysql://" + dbServer + "/" + dbName;
        Class.forName ("com.mysql.jdbc.Driver").newInstance ();
        dbconn = DriverManager.getConnection(dbUrl, dbUser, dbPass);
        System.out.println ("Database connection established");
    } catch (Exception e) {
        e.printStackTrace();
        System.err.println ("Cannot connect to database server");
        System.exit(-1);
    }
    
    System.out.println("  Downloading category: " + category);                  
    
    Statement s = null;
    try {
        s = dbconn.createStatement();
    } catch (SQLException e) {
        System.err.println ("Error on creating the statement");
        System.exit(-1);
        e.printStackTrace();
    }
    
    String q = "SELECT resource,topic FROM " + 
            "content_links " + 
            "WHERE topic LIKE 'Top/" + category + "%';";
    
    try {
        s.executeQuery(q);
    } catch(Exception e) {
        System.err.println ("Error on executing the SQL statement");
        System.exit(-1);
        e.printStackTrace();
    }
    
    ResultSet rs = null;
    try {
        rs = s.getResultSet ();
    } catch (SQLException e) {
        System.err.println ("Error on getting the result set");
        System.exit(-1);
        e.printStackTrace();
    }
    
    
    int count = 0, webError = 0;
    
    // work with the result set
    try {
        while (rs.next ()) {
    
            // MAIN LOOP
        }
    
    } catch (SQLException e) {
        System.err.println ("Error on getting next item");
        System.exit(-1);
        e.printStackTrace();
    }
    
    // Close connection to database
    if (dbconn != null) {
        try {
            dbconn.close ();
            System.out.println ("  Database connection terminated");
        } catch (Exception e) { /* ignore close errors */ }
    }
    
  2. HTTP 连接,提取网站标题和样板过滤器

    try {
    
        String title = "";
        org.jsoup.nodes.Document doc = Jsoup.connect(urlVal).get();
    
        for (Element element : doc.select("*")) {
            if (element.tagName().equalsIgnoreCase("title")) {
                title = element.text();
            }
            if (!element.hasText() && element.isBlock()) {
                element.remove();
            }
        }
    
        String contents = "";
        contents = NumWordsRulesExtractor.INSTANCE.getText(doc.text());                                         
        storeFile(id, urlVal, catVal, title, contents);
                }
    } catch (BoilerpipeProcessingException e) {
        System.err.println("Connection failed to: " + urlVal);
    } catch (MalformedURLException e1) {
        System.err.println("Malformed URL: " + urlVal);
    } catch(Exception e2) {
        System.err.println("Exception: " + e2.getMessage());
        e2.getStackTrace();
    }
    
  3. 写入文件

    private static void storeFile(String id, String url, String cat, String title, String contents) {
    BufferedWriter out = null;
    try {
        out = new BufferedWriter(
                new OutputStreamWriter(
                new FileOutputStream(
                new File(path + "/" + id + ".webtrec")),"UTF8"));
    
        // write in TREC format
        out.write("...");
    } catch (IOException e) {
        System.err.println("Error: " + e.getMessage());
        e.printStackTrace();
    } finally {
        try {
            out.close();
        } catch (IOException e) {
            System.err.println("Error: " + e.getMessage());
            e.printStackTrace();
        }
    }
    

【问题讨论】:

  • 你应该发布你得到的异常的堆栈跟踪。
  • 您多久调用一次“数据库访问”部分?什么时候关闭数据库连接?
  • @Flavio:我只执行该部分一次,因为 SQL 语句会查询给定类别的所有 URL。然后我循环遍历该while statement 中的结果集。数据库在执行结束时关闭,我将编辑问题并添加我错过的那部分代码。我会尝试获取该堆栈跟踪,但正如我所说,这需要我一段时间,可能需要几天!

标签: java file runtime-error


【解决方案1】:

是的。您正在泄漏文件描述符。

在第一种情况下,您打开一个数据库连接并且从不关闭它。连接通常会使用套接字或其他东西与数据库通信。由于你不关闭连接,所以套接字不会被关闭,你会泄漏一个文件描述符。

在第二种情况下,我怀疑对Jsoup.connect(urlVal) 的调用正在打开一个连接,然后您没有关闭该连接。这将导致文件描述符泄漏。

更正 - close() 接口上没有 close() 方法。看起来实际的连接必须由get 方法在内部创建然后关闭。假设是这样,则第二种情况没有文件描述符泄漏。

第三种情况不会泄漏文件描述符。但是,如果您无法打开文件,out.close(); 语句将尝试调用 null 上的方法...并抛出 NPE。


解决方案是找到所有您打开文件、数据库连接、http 连接的位置,并确保句柄始终关闭。

一种方法是将close() 调用(或等效调用)放在finally 块中......但请确保您不会在null 上意外调用close()

另一种方法是使用 Java 7 “try with resource”语法。例如:

private static void storeFile(String id, String url, String cat, 
                              String title, String contents) {
    try (BufferedWriter out = new BufferedWriter(
                new OutputStreamWriter(
                new FileOutputStream(
                new File(path + "/" + id + ".webtrec")),"UTF8"))) {
        // write in TREC format
        out.write("...");
        out.close();
    } catch (IOException e) {
        System.err.println("Error: " + e.getMessage());
        e.printStackTrace();
    }
}

(但请注意,Java 7 语法只能用于实现新的Closeable 接口的资源。)

【讨论】:

  • +1:对我帮助很大,谢谢。我会检查这一切。我认为 Jsoup 在其方法中关闭了它。我得好好检查一下..
  • 这很奇怪,我在Connection interface 中没有看到任何关闭方法... API
【解决方案2】:

补充斯蒂芬的综合分析。我建议为数据库使用连接池,尽管正如斯蒂芬指出的那样,除非您关闭这些连接,否则您将关闭池,但至少更容易发现原因......

我没有看到任何证据,但是您应该使用某种线程池来下载页面,这将有助于最大限度地利用系统资源。一些执行者服务就足够了。就像我说的那样,您可能已经这样做了,但您没有显示任何代码(或评论)。

【讨论】:

  • +1 - 连接池是个好主意......虽然它们实际上并不能解决这个问题。
  • 其实我没有实现连接池,也没有实现任何线程策略。您的意思是我可以在从数据库获得的结果集上使用线程吗?在处理完当前行之前,我似乎无法获得下一行...
  • @kits89 我会使用线程池来播种所有处理任务。也就是说,我会从数据库中读取所有行,对于每一行,我会在队列(执行器服务)上放置一个任务,该任务使用有限的线程池来处理队列中的任务。
  • @stephenc 是的,你的权利,ts 不会解决根本问题,它会做什么,如果他未能关闭/返回池,则阻止他创建/获取任何新连接.它隔离了问题
猜你喜欢
  • 2014-10-03
  • 2010-12-07
  • 1970-01-01
  • 2014-06-23
  • 2016-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多