Exception: Too many open files

sunny01

Aktives Mitglied
Hallo,

leider tritt bei meinem Java-Programm, welches alle Links einer Website besuchen soll, und dann ein Textfile mit dem Graphen der Verlinkung speichern soll, manchmal die Exception: "Too many open files" auf, wenn am Ende das File geschrieben werden soll (es speichert eine Adjazenzmatrix).

Gefühlsmäßig tritt der Fehler nur dann auf, wenn die jeweilige Webseite besonders viele tote Links, also nicht erreichbare Unterseiten hatte. Kann es aber nicht sicher sagen. Ich habe mich schon verrückt gesucht, und finde den Fehler nicht, wo etwas offen gelassen wird. Ich poste hier einmal die eventuell relevanten Codeteile (teilweise sind die Try-Catch-Blöcke bereits etwas komisch usw., das ist, weil ich schon so viel herumprobiert habe, um den Fehler zu beheben).

Code:
public class HTT2 implements Runnable {

    [...]

    public static void main(String[] args) throws Exception {
        try {
            (new Thread(new HTT2())).start();
        }
        catch (StackOverflowError e) {
            System.out.println("Exception: StackOverFlowError");
        }
        catch (Throwable t) {
            System.out.println("Error t: " + t);
        }
    }

    public void run() {
        startCrawling();
    }

    public void startCrawling() {
        try {
            [...]
            FileReader file = new FileReader(path + "data/links" + nr + ".txt");
            BufferedReader in = new BufferedReader(file);
            String line = null;

            while ((line = in.readLine()) != null) {
                urlList.add(line.toString());
            }
            in.close();
            in = null;
            file.close();
            file = null;

            while (!urlList.isEmpty()) {
                [...]
                getLinks(actUrl);
            }
        }
        catch (Exception e) {
            System.err.println("Exception: File with input links not found.");
        }
    }

    public void getLinks(URL url) {
        URLConnection con = null;
        InputStream stream = null;
        InputStreamReader input = null;
        BufferedReader br = null;
        HTMLEditorKit editorKit = null;
        HTMLDocument htmlDoc = null;
        [...]

        try {
            con = url.openConnection();
            con.setReadTimeout(10000);
            if (con.getContentType().contains("text/html")) { 
                stream = con.getInputStream();
                try {
                    input = new InputStreamReader(stream);
                    try {
                        br = new BufferedReader(input);
                        try {
                            editorKit = new HTMLEditorKit();
                            htmlDoc = new HTMLDocument();
                            htmlDoc.putProperty("IgnoreCharsetDirective", Boolean.TRUE);
                            editorKit.read(br, htmlDoc, 0);
                        }
                        finally {
                            br.close();
                        }
                    }
                    finally {
                        br.close();
                        input.close();
                    }
                }
                finally {
                    br.close();
                    input.close();
                    stream.close();
                }

                HTMLDocument.Iterator iter = htmlDoc.getIterator(HTML.Tag.A);
                [...]
                
                while (iter.isValid()) { //Iterate through all links of webpage
                    [...]
                    iter.next();
                }
            }
            con = null;
            htmlDoc = null;
            editorKit = null;
            br = null;
            input = null;
            stream = null;
        }
        catch (Exception e) {
            System.err.println("Exception: Site doesn't exist - " + e);
            con = null;
            htmlDoc = null;
            editorKit = null;
            try {
                stream.close();
            }
            catch (Exception e1) {
                System.err.println("Exception closing: " + e1);
            }
            finally {
                stream = null;
                try {
                    input.close();
                }
                catch (Exception e2) {
                    System.err.println("Exception closing: " + e2);
                }
                finally {
                    input = null;
                    try {
                        br.close();
                    }
                    catch (Exception e3) {
                        System.err.println("Exception closing: " + e3);
                    }
                    finally {
                        br = null;
                    }
                }
            }
        }
        finally {
            getNextUrl();
        }
    }

    public void getNextUrl() {
        if (linksNotYetVisited.size() > 0) {
            [...]
            getLinks(newUrl);
        }
        else {
            [...]
            writeTextFile(str, pth);
        }
    }

    public void writeTextFile(String string, String path) {
        try {
            FileWriter file = new FileWriter(path);
            BufferedWriter out = new BufferedWriter(file);
            out.write(string);
            out.close();
            out = null;
            file.close();
            file = null;
        }
        catch (Exception e) {
            System.err.println("Exception: Error writing textfile.");
            System.err.println(e);
        }
    }
}

Das Ganze startet in einem neuen Thread, da ansonsten leider die Parameter für die Angabe der Stacksize als Parameter für die Virtual Machine (-Xss) beim Starten nicht greifen. Ansonsten wäre der Thread eigentlich nicht nötig, aber ich glaube auch nicht, dass er damit etwas zu tun hat.

Das Programm läuft auf einem Großrechner mit Linux (64bit-Architektur) und unter Java 5, da es unter Java 6 grobe Probleme gab (siehe auch mein anderer Forums-Thread).

Der Fehler tritt wie gesagt nicht immer auf ... und ich habe einfach keine Idee mehr, was ich zu schließen übersehen habe.
Sieht irgendjemand meinen Fehler?

Lg
sunny
 
Zuletzt bearbeitet:
Verbindungen (Sockets, Files, Reader, ...) immer sicher (im finally-Block) beenden, und nur dort....

Logge ruhig mal mit, wenn du einen File oder eine Socketverbindung aufmachst und wieder schließt, evtl. mit einem Zähler. Logging ist extrem wichtich um zu sehen, wie sich deine Applikation verhält. Ich empfehle log4j, aber auch das Logging des JDK ist besser als kein Logging.

Entweder du verpasst das Schließen von Ressourcen, oder du machst schlicht und ergreifend zu viele gleichzeitig auf (evtl. durch Rekursion). Da bleibt dann nur den Algorithmus anpassen oder die maximale Anzahl erhöhen... lieber das Erstere...

Tipp: bevor du Postest solltest du nicht rumprobieren und raten sondern Daten sammeln und diese hier präsentieren (wenn dann ein Forenthread überhaupt noch notwendig ist). Dazu gehört auch immer ein Stacktrace, wenn es einen gibt... und die Stelle im Code...
 
>Verbindungen (Sockets, Files, Reader, ...) immer sicher (im finally-Block) beenden, und nur dort....

Auf Linux kent mans ja: everything is a file...hatten das Problem erst kürzlich mit Axis2.

Auch im finally Block brauchts ein try catch!! Wenn bei einem von x close ne Exception fliegt wars das bei dir.
Nimm Apache Commons IO: IOUtils
oder
Java:
public static final void safeClose(Closeable c) { 
if (c != null) { try { c.close(); } catch(IOException e) {/* silent */}}
}
 
Hallo,

ihr habt natürlich Recht, das mit dem Herumprobieren führt zu keiner Lösung. Trotzdem habe ich versucht, die Änderungen vorzunehmen, die ihr mir vorgeschlagen habt.

Es hat sich aber soweit nichts geändert.

Der StackTrace:

Code:
java.io.FileNotFoundException: [...].txt (Too many open files)
	at java.io.FileOutputStream.open(Native Method)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:179)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:70)
	at java.io.FileWriter.<init>(FileWriter.java:46)
	at HTT2.writeTextFile(HTT2.java:432)
	at HTT2.writeMatrix(HTT2.java:426)
	at HTT2.writeAdjMatrix(HTT2.java:360)
	at HTT2.getNextUrl(HTT2.java:332)
	at HTT2.getLinks(HTT2.java:313)
	at HTT2.getNextUrl(HTT2.java:324)

Code:
    public void getLinks(URL url) {
        URLConnection con = null;
        InputStream stream = null;
        InputStreamReader input = null;
        BufferedReader br = null;
        HTMLEditorKit editorKit = null;
        HTMLDocument htmlDoc = null;
        
        [...]

        try {
            con = url.openConnection();
            con.setReadTimeout(10000);
            if (con.getContentType().contains("text/html")) { //Do only read file if it's of content-type text/html
                stream = con.getInputStream();
                try {
                    input = new InputStreamReader(stream);
                    try {
                        br = new BufferedReader(input);
                        try {
                            editorKit = new HTMLEditorKit();
                            htmlDoc = new HTMLDocument();
                            htmlDoc.putProperty("IgnoreCharsetDirective", Boolean.TRUE);
                            editorKit.read(br, htmlDoc, 0);
                        }
                        finally {
                            safeClose(br);
                        }
                    }
                    finally {
                        safeClose(br);
                        safeClose(input);
                    }
                }
                finally {
                    safeClose(br);
                    safeClose(input);
                    safeClose(stream);
                }

                HTMLDocument.Iterator iter = htmlDoc.getIterator(HTML.Tag.A);
                
                [...]
                
                while (iter.isValid()) {
                    [...]
                    iter.next();
                }
            }
        }
        catch (Exception e) {
            System.err.println("Exception: Site doesn't exist - " + e);
        }
        finally {
            safeClose(stream);
            safeClose(input);
            safeClose(br);
            [...]
            getNextUrl();
        }
    }

    public void getNextUrl() {
        if (linksNotYetVisited.size() > 0) {
            try {
                [...]
                getLinks(newUrl);
            }
            catch (Exception e) {
                System.err.print("Exception: Error getting next Url: " + newUrl);
                getNextUrl();
            }
        }
        else {
            writeAdjMatrix();
        }
    }

    public void adjMatrix() {
        [...]
        writeMatrix(m, path);
    }


    public void writeMatrix(int[][] matrix, String path) {
        [...]
        writeTextFile(str, path);
    }


    public void writeTextFile(String string, String path) {
        try {
            FileWriter file = new FileWriter(path);
            BufferedWriter out = new BufferedWriter(file);

            try {
                out.write(string);
            }
            catch (IOException ioe) {
                System.err.println("Exception: Error writing textfile: " + ioe);
            }
            finally {
                safeClose(out);
                safeClose(file);
                out = null;
                file = null;
            }
        }
        catch (Exception e) { 
            System.err.println("Exception: Error writing textfile.");
            e.printStackTrace();
        }
    }

    public static final void safeClose(Closeable c) {
        if (c != null) {
            try {
                c.close();
            }
            catch (IOException e) {/* silent */

            }
        }
    }
}

Der Fehler tritt beim Aufruf der Funktion writeTextFile() auf und es wird obenstehende Exception geworfen (äußerer try-catch-block).

Wie kann ich das mit dem Logging machen?
Die Rekursionstiefe ist sehr groß, es ist generell schwer, "mitzuschauen", da ein Durchlauf schon Weilchen dauert, und der Fehler auch nur bei manchen Webseiten auftritt, und nicht bei allen. Beispielsweise habe ich gerade eine Webseite fertigausgelesen, die mehr als 50.000 Unterknoten hatte, und da wurde das File geschrieben und keine Exception geworfen. Die Website, mit der es nicht funktioniert, hat nur ca. 5.000 Unterseiten.

Daher auch meine Vermutung anfangs, dass irgendwo das Exceptionhandling nicht stimmt, da ich das Gefühl habe, es passiert bei Seiten, die viele tote Links haben, wo dann also eine FileNotFoundException auftritt (die Zeile mit: System.err.println("Exception: Site doesn't exist - " + e);). Sicher bin ich aber auch da nicht.

Ich weiß dass meine ganzen Versuche hier stümperhaft sind und ich nicht viel Ahnung von dem Ganzen habe. Vielleicht kann mir jemand einen Link oder eine kleine Hilfestellung dazugeben, wie das mit dem Logging funktionieren würde.

Danke und liebe Grüße
sunny
 
Zuletzt bearbeitet:
räum das ganze mal auf!!. Da wimmelst ja von finally blöcken:
zb:
Java:
public void writeTextFile(String string, String path) {
	FileWriter file = null;
	BufferedWriter out = null;
	try {
		file = new FileWriter(path);
		out = new BufferedWriter(file);
		out.write(string);
	} catch (IOException e) {
		System.err.println("Exception: Error writing textfile.");
		e.printStackTrace();
	} finally {
		safeClose(out);
		safeClose(file);
	}
}
 
Zuletzt bearbeitet:
Hab ich gemacht ... die ganzen einzelnen finally-Blöcke habe ich auch erst nachträglich eingefügt, weil ich dachte, vielleicht muss ich sämtlich try-catch-finally-Blöcke auftrennen.

An der UrlConnection kann es nicht irgendwie liegen, oder? Dass diese noch explizit irgendwie geschlossen werden muss? Eigentlich nicht ... weil dann würde es wohl mit gar keine Webseite funktionieren ... es sind ja nur bestimmte, wo es nicht geht ...
 
Joe lag ziemlich sehr richtig: in Linux ist alles ein File...

Grob gesagt: ein Betriebsystem kann eine Anzahl n offener "Verbindungen" ("Files") bedienen. Jede "Verbindung" benötigt ein Handle und eben diese Anzahl ist begrenzt.
Mit Logging und/oder Monitoring (der Applikation oder des BS) bekommst du heraus wie viele Ressourcen du benötigst und kannst das dann gegenüber den maximal zur Verfügungs stehenden abgleichen.

Wenn deine Vermutung richtig ist, dass das bei Seiten mit vielen toten Links eher auftritt, dann liegt die Vermutung nahe, dass durch einen Fehler (Exception) im Zusammenhang mit toten Links Ressourcen nicht freigegeben werden.
Deine Vermutung ist aber nur eine Vermutung... somit ist das alles auch nur geraten.

[EDIT]
Mit JVisualVM oder mit einem Profiler kannst du dir mal einen komplette VM Dump ansehen und ein wenig monitoren. Da sieht man auch, welche Objekte (FileStream, Connection usw.) sehr häufig vorkommen und wo diese hängen.
Viele solche Objekte deuten darauf hin, dass der Algorithmus so unpraktisch ist und einfach viele Ressourcen gleichzeitig benötigt. Das gilt es dann zu optimieren. Wenige solcher Objekte deuten darauf hin, dass die Systemressourcen nicht sauber frei gegeben werden.
 
Zuletzt bearbeitet:

Zurück
Oben