打开文件太多错误

时间:2012-08-27 09:59:09

标签: java file runtime-error

简介

我在网上找到了很多关于太多打开文件异常的信息,但我无法解决这个奇怪的情况。正如我所读到的,当超过OS中定义的进程打开的文件描述符的数量时,抛出异常。这些文件的性质是多种多样的。文件可以是套接字,文档等。我已经找到了强大而安全的方法来打开我在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();
        }
    }
    

2 个答案:

答案 0 :(得分:3)

烨。您正在泄漏文件描述符。

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

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

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

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


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

实现此目的的一种方法是将close()调用(或等效内容)置于finally块中...但请确保您不会在close()上意外调用null {1}}。

另一种方法是使用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 :(得分:2)

添加到Stephen的综合分析中。我建议为数据库使用连接池,但是,正如斯蒂芬指出的那样,除非你关闭这些连接,否则你会把游泳池关闭,但至少会更容易发现原因......

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