1

序章

開いているファイルが多すぎるという例外に関する多くの情報をWeb で見つけましたが、この奇妙なケースを解決できませんでした。私が読んだように、OSで定義されたプロセスによって開かれたファイル記述子の数を超えると、例外がスローされます。これらのファイルの性質はさまざまです。ファイルは、ソケット、ドキュメントなどである可能性があります。そして、Java アプリケーションに実装したファイルを開くための堅牢で安全な方法を見つけました。

このアプリケーションは、ボイラーパイプアルゴリズムを使用して Web ページをダウンロードする短いプログラムです。このようにして、そのサイトの最も代表的なコンテンツを取得します。そして、それを適切な形式 ( TREC 形式) でディスクに書き込みます。これらの Web サイトの URL は、JDBC コネクタを使用してアクセスする MySQL データベースから取得されます。

したがって、例外は 3 つの異なる場所からスローされる可能性があると思います。

  • データベースへの接続
  • Web サイトへの HTTP 接続
  • ファイルのオープンと書き込み

とはいえ、私が言ったように、私はそれらのファイルを開いて書き込む正しい方法を使用していると思います。

問題

処理する URL が数千あり、しばらくすると例外がスローされます (これにより、デバッグも非常に困難になります... )。それが問題かどうかはわかりませんが、URL はさまざまなカテゴリに分類されており、プロセス全体を高速化するためにプログラムのさまざまなインスタンスを実行しています。カテゴリは重複しないので問題ありません。

コード

読みやすくするために、コードのこれら 3 つの部分だけを簡略化して示します。

  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();
        }
    }
    
4

2 に答える 2

3

うん。ファイル記述子をリークしています。

最初のケースでは、DB 接続を開き、決して閉じません。接続は通常、ソケットなどを使用してデータベースと通信します。接続を閉じないため、ソケットは閉じられず、ファイル記述子がリークします。

2 番目のケースでは、への呼び出しがJsoup.connect(urlVal)接続を開いていると思われますが、それを閉じません。これにより、ファイル記述子のリークが発生します。

訂正 -インターフェイスclose()にメソッドがありません。Connection実際の接続を作成してから、getメソッドによって内部的に閉じる必要があるようです。そうであると仮定すると、2 番目のケースではファイル記述子のリークはありません。

3 番目のケースでは、ファイル記述子がリークしません。ただし、ファイルを開くことができない場合、out.close();ステートメントは ... でメソッドを呼び出そうとしnull、NPE をスローします。


解決策は、ファイル、データベース接続、http 接続を開いているすべての場所を見つけ、ハンドルが常に閉じていることを確認することです。

それを行う 1 つの方法は、close()呼び出し (または同等のもの) をブロックに入れることですが、誤って on を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インターフェースを実装するリソースでのみ使用できることに注意してください。)

于 2012-08-27T10:10:50.933 に答える
2

スティーブンの包括的な分析に追加します。データベースに接続プールを使用することをお勧めしますが、スティーブンが指摘したように、これらの接続を閉じない限り、プールを閉じますが、少なくともその理由を発見するのは簡単です...

証拠は見たことがありませんが、何らかのスレッド プールを使用してページをダウンロードする必要があります。これは、システムのリソースを最大化するのに役立ちます。エグゼキューターサービスの一部で十分です。私が言ったように、あなたはおそらくすでにこれを行っていますが、コード (またはコメント) を示していません。

于 2012-08-27T10:15:55.910 に答える