Java IOException “For mange åbne filer”

1. Introduktion

En almindelig faldgrube, når du arbejder med filer i Java, er muligheden for at løbe tør for tilgængelige filbeskrivere.

I denne vejledning ser vi på denne situation og tilbyder to måder at undgå dette problem på.

2. Hvordan JVM håndterer filer

Selvom JVM gør et fremragende stykke arbejde med at isolere os fra operativsystemet, delegerer den operationer på lavt niveau som filhåndtering til operativsystemet.

Dette betyder, at for hver fil, vi åbner i et Java-program, tildeler operativsystemet en filbeskrivelse, der relaterer filen til vores Java-proces. Når JVM er færdig med filen, frigiver den deskriptoren.

Lad os nu dykke ned i, hvordan vi kan udløse undtagelsen.

3. Lækende filbeskrivere

Husk, at vi for hver filreference i vores Java-applikation har en tilsvarende filbeskrivelse i operativsystemet. Denne deskriptor lukkes kun, når filreferenceinstansen bortskaffes. Dette vil ske under skraldopsamlingsfasen.

Imidlertid, hvis referencen forbliver aktiv, og flere og flere filer åbnes, vil OS til sidst løbe tør for filbeskrivere at allokere. På det tidspunkt vil den videresende denne situation til JVM, hvilket vil resultere i en IOUndtagelse bliver kastet.

Vi kan gengive denne situation med en kort enhedstest:

@Test offentlig ugyldig nårNotClosingResoures_thenIOExceptionShouldBeThrown () {prøv {for (int x = 0; x <1000000; x ++) {FileInputStream leakyHandle = ny FileInputStream (tempFile); } fail ("Metoden burde have mislykkedes"); } fange (IOException e) {assertTrue (e.getMessage (). containIgnoreCase ("for mange åbne filer")); } fange (Undtagelse e) {fail ("Uventet undtagelse"); }} 

På de fleste operativsystemer løber JVM-processen ud af filbeskrivere, inden sløjfen afsluttes og derved udløser IOUndtagelse.

Lad os se, hvordan vi kan undgå denne tilstand med korrekt ressourcehåndtering.

4. Håndtering af ressourcer

Som vi sagde før, frigives filbeskrivere ved JVM-processen under Garbage Collection.

Men hvis vi ikke lukkede vores filreference ordentligt, kan samleren muligvis ikke ødelægge referencen på det tidspunkt og lade beskriveren være åben og begrænse antallet af filer, vi kunne åbne.

Vi kan dog let fjerne dette problem ved at sikre, at hvis vi åbner en fil, sikrer vi, at vi lukker den, når vi ikke længere har brug for den.

4.1. Manuel frigivelse af referencer

Manuel frigivelse af referencer var en almindelig måde at sikre korrekt ressourcehåndtering inden JDK 8.

Ikke kun gøre vi skal eksplicit lukke den fil, vi åbner, men sørg også for, at vi gør det, selvom vores kode mislykkes og kaster undtagelser. Dette betyder at bruge langt om længe nøgleord:

@Test offentlig ugyldig nårClosingResoures_thenIOExceptionShouldNotBeThrown () {prøv {for (int x = 0; x <1000000; x ++) {FileInputStream nonLeakyHandle = null; prøv {nonLeakyHandle = ny FileInputStream (tempFile); } endelig {if (nonLeakyHandle! = null) {nonLeakyHandle.close (); }}}} fangst (IOException e) {assertFalse (e.getMessage (). toLowerCase (). indeholder ("for mange åbne filer")); fail ("Metoden burde ikke have mislykkedes"); } fange (Undtagelse e) {fail ("Uventet undtagelse"); }} 

Som den langt om længe Blok altid udføres, det giver os chancen for at lukke vores reference korrekt og derved begrænse antallet af åbne deskriptorer.

4.2. Ved brug af prøv med ressourcer

JDK 7 giver os en renere måde at udføre bortskaffelse af ressourcer på. Det er almindeligt kendt som prøv med ressourcer og giver os mulighed for at delegere bortskaffelse af ressourcer ved at inkludere ressourcen i prøve definition:

@Test offentlig ugyldig nårUsingTryWithResoures_thenIOExceptionShouldNotBeThrown () {prøv {for (int x = 0; x <1000000; x ++) {prøv (FileInputStream nonLeakyHandle = ny FileInputStream (tempFile)) {// gør noget med filen}}} fangst (IOEx ) {assertFalse (e.getMessage (). toLowerCase (). indeholder ("for mange åbne filer")); fail ("Metoden burde ikke have mislykkedes"); } fange (Undtagelse e) {fail ("Uventet undtagelse"); }}

Her erklærede vi nonLeakyHandle inde i prøve udmelding. På grund af dette lukker Java ressourcen for os i stedet for at vi behøver at bruge langt om længe.

5. Konklusion

Som vi kan se, kan manglende korrekt lukning af åbne filer føre os til en kompleks undtagelse med forgreninger overalt i vores program. Med korrekt ressourcehåndtering kan vi sikre, at dette problem aldrig vil opstå.

Den komplette kildekode til artiklen er tilgængelig på GitHub.