Java IOException „Túl sok megnyitott fájl”
1. Bemutatkozás
A Java fájlokkal való munka során gyakori buktató a rendelkezésre álló fájlleírók elfogyásának lehetősége.
Ebben az oktatóanyagban megvizsgáljuk ezt a helyzetet, és két módot kínálunk a probléma elkerülésére.
2. Hogyan kezeli a JVM a fájlokat
Bár a JVM kiváló munkát végez az operációs rendszertől való elszigeteléssel, az operációs rendszert olyan alacsony szintű műveletekre ruházza át, mint a fájlkezelés.
Ez azt jelenti, hogy minden egyes Java alkalmazásban megnyitott fájlhoz az operációs rendszer egy fájlleírót rendel ki, amely a fájlt a Java folyamatunkhoz kapcsolja. Miután a JVM befejezte a fájlt, felszabadítja a leírót.
Most merüljünk el abban, hogy miként válthatjuk ki a kivételt.
3. Szivárgó fájlleírók
Emlékezzünk vissza arra, hogy a Java alkalmazás minden fájl hivatkozására az operációs rendszerben van egy megfelelő fájlleíró. Ez a leíró csak akkor lesz bezárva, ha a fájl hivatkozási példánya megsemmisül. Ez a Szemétgyűjtés szakaszában fog megtörténni.
Ha azonban a hivatkozás továbbra is aktív marad, és egyre több fájl van megnyitva, akkor az operációs rendszer végül kifog a fájlleírókból. Ezen a ponton továbbítja ezt a helyzetet a JVM-nek, ami egy IOException dobják.
Rövid egységvizsgálattal reprodukálhatjuk ezt a helyzetet:
@Test public void whenNotClosingResoures_thenIOExceptionShouldBeThrown () {try {for (int x = 0; x <1000000; x ++) {FileInputStream leakyHandle = új FileInputStream (tempFile); } fail ("A módszernek sikertelennek kellett lennie"); } catch (IOException e) {assertTrue (e.getMessage (). tartalmazIgnoreCase ("túl sok megnyitott fájl")); } catch (e kivétel) {fail ("Váratlan kivétel"); }}
A legtöbb operációs rendszeren a JVM folyamatban a ciklus befejezése előtt elfogynak a fájlleírók, ezáltal elindítva a IOException.
Nézzük meg, hogyan kerülhetjük el ezt az állapotot az erőforrások megfelelő kezelésével.
4. Források kezelése
Mint korábban mondtuk, a fájlleírókat a JVM folyamat adja ki a Szemétgyűjtés során.
De ha nem zártuk be megfelelően a fájl referenciánkat, akkor a gyűjtő dönthet úgy, hogy akkor nem rombolja le a referenciát, nyitva hagyva a leírót és korlátozva a megnyitható fájlok számát.
Ezt a problémát azonban könnyen eltávolíthatjuk azzal, hogy megbizonyosodunk arról, hogy ha megnyitunk egy fájlt, akkor bezárjuk, ha már nincs rá szükségünk.
4.1. Hivatkozások kézi felszabadítása
A referenciák kézi kiadása a JDK 8 előtti általános erőforrás-kezelés biztosításának általános módja volt.
Nem csak kifejezetten be kell zárnunk a megnyitott fájlokat, de arról is gondoskodjon, hogy akkor is megtesszük, ha a kódunk meghiúsul, és kivételeket vet fel. Ez azt jelenti, hogy a végül kulcsszó:
@Test public void whenClosingResoures_thenIOExceptionShouldNotBeThrown () {try {for (int x = 0; x <1000000; x ++) {FileInputStream nonLeakyHandle = null; próbáld ki a {nonLeakyHandle = új FileInputStream (tempFile) fájlt; } végül {if (nonLeakyHandle! = null) {nonLeakyHandle.close (); }}}} catch (IOException e) {assertFalse (e.getMessage (). toLowerCase (). tartalmaz ("túl sok nyitott fájlt")); sikertelen ("A módszernek nem kellett volna kudarcot vallania"); } catch (e kivétel) {fail ("Váratlan kivétel"); }}
Mivel a végül A blokk mindig végrehajtásra kerül, ez lehetőséget ad arra, hogy megfelelően lezárjuk referenciánkat, ezáltal korlátozzuk a nyitott leírók számát.
4.2. Használata erőforrásokkal próbálkozzon
A JDK 7 tisztább módot kínál számunkra az erőforrások ártalmatlanításának elvégzéséhez. Közismert nevén erőforrásokkal próbálkozzon és lehetővé teszi számunkra, hogy az erőforrások elidegenítését az erőforrásnak a próbáld ki meghatározás:
@Test public void whenUsingTryWithResoures_thenIOExceptionShouldNotBeThrown () {try {for (int x = 0; x <1000000; x ++) {try (FileInputStream nonLeakyHandle = new FileInputStream (tempFile)) {// csináljon valamit a fileExcepcióval ) {assertFalse (e.getMessage (). toLowerCase (). tartalmaz ("túl sok megnyitott fájlt")); sikertelen ("A módszernek nem kellett volna kudarcot vallania"); } catch (e kivétel) {fail ("Váratlan kivétel"); }}
Itt jelentettük ki nonLeakyHandle benne próbáld ki nyilatkozat. Emiatt a Java bezárja az erőforrást számunkra, ahelyett, hogy használnunk kellene végül.
5. Következtetés
Mint láthatjuk, a nyitott fájlok megfelelő bezárásának elmulasztása komplex kivételhez vezethet minket, programunk egészére kiterjedő következményekkel. Megfelelő erőforrás-kezeléssel biztosíthatjuk, hogy ez a probléma soha ne jelentkezzen.
A cikk teljes forráskódja elérhető a GitHub oldalon.