Ich arbeite an einer riesigen veralteten Java-Anwendung mit vielen handgeschriebenen Sachen, die Sie heutzutage einem Framework überlassen könnten.
Das Problem, mit dem ich gerade konfrontiert bin, ist, dass die Dateizugriffsnummern auf unserem Solaris-Server knapp werden. Ich würde gerne wissen, wie Sie geöffnete Dateigriffe am besten nachverfolgen können. Wo kann man nachsehen und was kann dazu führen, dass offene Dateigriffe auslaufen?
Ich kann die Anwendung nicht unter Solaris debuggen, nur in meiner Windows-Entwicklungsumgebung. Ist es überhaupt sinnvoll, die offenen Dateigriffe unter Windows zu analysieren?
Eine gute Sache, die ich gefunden habe, um nicht geschlossene Datei-Handles aufzuspüren, ist FindBugs:
Es prüft viele Dinge, aber eine der nützlichsten ist das Öffnen / Schließen von Ressourcen. Es ist ein statisches Analyseprogramm, das auf Ihrem Quellcode läuft und es ist auch als Eclipse-Plugin verfügbar.
Um den zweiten Teil der Frage zu beantworten:
Was kann dazu führen, dass geöffnete Dateigriffe auslaufen?
Offensichtlich viele Dateien öffnen und dann nicht schließen.
Das einfachste Szenario besteht darin, dass die Verweise auf alle Objekte, die die systemeigenen Handles enthalten (z. B. FileInputStream
), vor dem Schließen verworfen werden. Dies bedeutet, dass die Dateien geöffnet bleiben, bis die Objekte abgeschlossen sind.
Die andere Option ist, dass die Objekte irgendwo gespeichert und nicht geschlossen werden. Ein Heap-Dump könnte Ihnen sagen, was wo verweilt ( jmap
und jhat
sind im JDK enthalten oder Sie kann jvisualvm
verwenden, wenn Sie eine GUI wünschen. Sie sind wahrscheinlich daran interessiert, nach Objekten zu suchen, die FileDescriptor
besitzen .
Dieses kleine Skript hilft mir, die Anzahl der geöffneten Dateien im Auge zu behalten, wenn ich die Anzahl der Test-ICs benötige. Wenn es unter Linux verwendet wurde, sollten Sie es für Solaris (möglicherweise :)) patchen.
%Vor%Sie können auch versuchen, mit jvm ontion -Xverify: none zu spielen - es sollte jar verification deaktivieren (wenn die meisten geöffneten Dateien jars sind ...). Für Lecks durch nicht geschlossen FileOutputStream können Sie findbug (Mentored oben) oder versuchen, Artikel zu finden, wie Standard-Java-FileOutputStream / FileInputStream Patch, wo Sie sehen können, wer Dateien öffnen und vergessen, sie zu schließen. Leider kann ich diesen Artikel nicht finden, aber das ist vorhanden :) Denken Sie auch über die Erhöhung von Filelimit - für aktuelle * nix-Kernel ist kein Problem mehr als 1024 fd handhaben.
Das mag in Ihrem Fall nicht praktisch sein, aber was ich einmal getan habe, als ich ein ähnliches Problem mit offenen Datenbankverbindungen hatte, war das Überschreiben der "offenen" Funktion mit meinen eigenen. (Praktisch hatte ich diese Funktion schon, weil wir unser eigenes Verbindungs-Pooling geschrieben hatten.) In meiner Funktion habe ich dann einen Eintrag zu einer Tabelle hinzugefügt, die das Öffnen aufzeichnet. Ich habe einen Stack-Trace-Aufruf durchgeführt und die Kennung des Anrufers gespeichert, zusammen mit der Uhrzeit, die ich angerufen habe, und ich vergesse was sonst noch. Als die Verbindung freigegeben wurde, löschte ich den Tabelleneintrag. Dann hatte ich einen Bildschirm, auf dem wir die Liste der offenen Einträge ablegen konnten. Dann könnten Sie sich den Zeitstempel ansehen und leicht erkennen, welche Verbindungen für einen unwahrscheinlichen Zeitraum geöffnet waren und welche Funktionen diese öffnen.
Daraus konnten wir die Funktionen, die Verbindungen öffneten und sie nicht schließen konnten, schnell aufspüren.
Wenn Sie viele offene Dateigriffe haben, ist es wahrscheinlich, dass Sie sie nicht schließen können, wenn Sie irgendwo fertig sind. Sie sagen, Sie haben nach richtigen try / finally-Blöcken gecheckt, aber ich würde vermuten, dass Sie irgendwo im Code einen schlechten Code verpasst haben, oder Sie haben eine Funktion, die reicht und es nie bis zum Schluss schafft. Ich nehme an, es ist auch möglich, dass Sie wirklich jedes Mal, wenn Sie eine Datei öffnen, ordentliche Schlüsse machen, aber Sie öffnen Hunderte von Dateien gleichzeitig. Wenn das der Fall ist, bin ich nicht sicher, was Sie tun können, abgesehen von einem ernsthaften Redesign des Programms, um weniger Dateien zu manipulieren, oder einem ernsthaften Redesign des Programms, um Ihre Dateizugriffe in die Warteschlange zu stellen. (An dieser Stelle füge ich das übliche hinzu: "Ohne die Details Ihrer Anwendung zu kennen, etc.)
Es lohnt sich, daran zu denken, dass offene Sockets auch Dateihandles auf Unix-Systemen belegen. Es könnte also sehr wohl etwas wie ein Datenbank-Verbindungspool-Leck sein (zB offene Datenbankverbindungen, die nicht geschlossen und zum Pool zurückgebracht werden), die zu diesem Problem führen - sicherlich habe ich diesen Fehler vor einem Leck im Verbindungspool gesehen. p>
Ich würde damit beginnen, meinen Systemadministrator zu bitten, eine Liste aller offenen Dateideskriptoren für den Prozess zu bekommen. Verschiedene Systeme tun dies auf verschiedene Arten: Linux zum Beispiel hat das Verzeichnis /proc/PID/fd
. Ich erinnere mich, dass Solaris einen Befehl hat (vielleicht pfiles ?), Der dasselbe tut - Ihr Systemadministrator sollte es wissen.
Wenn Sie jedoch nicht viele Verweise auf dieselbe Datei sehen, wird Ihnen eine fd-Liste nicht helfen. Wenn es sich um einen Serverprozess handelt, sind wahrscheinlich viele Dateien (und Sockets) aus einem bestimmten Grund geöffnet. Die einzige Möglichkeit zur Behebung des Problems besteht in der Anpassung des Systemlimits für geöffnete Dateien - Sie können das Limit pro Benutzer auch mit ulimit überprüfen, in den meisten aktuellen Installationen entspricht dies jedoch dem Systemlimit.
Es handelt sich nicht um eine direkte Antwort auf Ihre Frage, aber diese Probleme können darauf zurückzuführen sein, dass Dateiressourcen in Ihrem alten Code nicht ordnungsgemäß freigegeben wurden. Wenn Sie beispielsweise mit FileOutputStream-Klassen arbeiten, stellen Sie sicher, dass die close-Methoden in einem finally-Block wie in diesem Beispiel aufgerufen werden:
%Vor%Ich würde die Umgebungseinstellungen in Ihrer Solaris-Box überprüfen. Ich glaube, dass Solaris standardmäßig nur 256 Dateizugriffe pro Prozess erlaubt. Für eine Serveranwendung, insbesondere wenn sie auf einem dedizierten Server ausgeführt wird, ist dies sehr gering. Abbildung 50 oder mehr Deskriptoren zum Öffnen von JRE und Bibliothek JARs, und dann mindestens ein Deskriptor für jede eingehende Anfrage und Datenbankabfrage, wahrscheinlich mehr, und Sie können sehen, wie dies nur nicht Senf für ein seriöser Server.
Sehen Sie sich die Datei /etc/system
für die Werte von rlim_fd_cur
und rlim_fd_max
an, um zu sehen, was Ihr System eingestellt hat. Dann überlegen Sie, ob dies sinnvoll ist (Sie können sehen, wie viele Dateideskriptoren geöffnet sind, während der Server mit dem Befehl lsof
läuft, idealerweise mit dem Parameter -p [Prozess-ID].
Dies ist ein Codierungsmuster, mit dem Sie nicht geschlossene Ressourcen finden können. Es schließt die Ressourcen und klagt auch im Protokoll über das Problem.
%Vor%Umbrechen Sie die obigen file.close () - Aufrufe in try-catch-Blöcken, die Fehler ignorieren.
Außerdem verfügt Java 7 über eine neue Funktion zum Testen mit Ressourcen, mit der Ressourcen automatisch geschlossen werden können.
Es könnte Ihnen sicherlich eine Idee geben. Da es Java ist, sollte die Datei öffnen / schließen-Mechanik ähnlich implementiert werden (es sei denn, eine der JVMs ist falsch implementiert). Ich würde empfehlen, Datei-Monitor auf Windows zu verwenden.