Java >> Java tutorial >  >> Tag >> final

Skal Java 9 Cleaner foretrækkes frem for færdiggørelse?

Det er ikke meningen, at du skal erstatte alle finalize() metoder med en Cleaner . Det faktum, at udfasningen af ​​finalize() metode og introduktionen af ​​(en public ) Cleaner skete i den samme Java-version, indikerer kun, at et generelt arbejde om emnet er sket, ikke at det ene skal være en erstatning for det andet.

Andet relateret arbejde i den Java-version er fjernelsen af ​​reglen om, at en PhantomReference ryddes ikke automatisk (ja, før Java 9, ved hjælp af en PhantomReference i stedet for finalize() krævede stadig to GC-cyklusser for at genvinde objektet) og introduktionen af ​​Reference.reachabilityFence(…) .

Det første alternativ til finalize() , er slet ikke at have en renovationsafhængig drift. Det er godt, når du siger, at du ikke har mange, men jeg har set fuldstændig forældede finalize() metoder i naturen. Problemet er, at finalize() ligner en almindelig protected metode og den seje myte, at finalize() var en slags destruktor stadig spredt på nogle internetsider. Markerer det som udfaset giver mulighed for at signalere til udvikleren, at dette ikke er tilfældet, uden at bryde kompatibiliteten. Brug af en mekanisme, der kræver eksplicit registrering, hjælper med at forstå, at dette ikke er det normale programflow. Og det gør ikke ondt, når det ser mere kompliceret ud end at tilsidesætte en enkelt metode.

Hvis din klasse indkapsler en ikke-heap-ressource, står der i dokumentationen:

Klasser, hvis forekomster indeholder ressourcer, der ikke er heap, bør give en metode til at muliggøre eksplicit frigivelse af disse ressourcer, og de bør også implementere AutoCloseable, hvis det er relevant.

(så det er den foretrukne løsning)

Cleaner og PhantomReference giver mere fleksible og effektive måder at frigive ressourcer på, når et objekt bliver utilgængeligt.

Så når du virkelig har brug for interaktion med skraldesamleren, navngiver selv denne korte dokumentationskommentar to alternativer som PhantomReference er ikke nævnt som den skjulte-for-udvikler-backend af Cleaner her; ved hjælp af PhantomReference direkte er et alternativ til Cleaner , som kan være endnu mere kompliceret at bruge, men også giver endnu mere kontrol over timing og tråde, herunder muligheden for at rydde op inden for den samme tråd, som brugte ressourcen. (Sammenlign med WeakHashMap , som har en sådan oprydning, der undgår udgifterne til gevindsikre konstruktioner). Det gør det også muligt at håndtere undtagelser, der er smidt under oprydningen, på en bedre måde end at sluge dem i stilhed.

Men endda Cleaner løser flere problemer, som du er opmærksom på.

Et væsentligt problem er tidspunktet for registrering.

  • Et objekt i en klasse med en ikke-triviel finalize() metode er registreret, når Object() konstruktør er blevet udført. På dette tidspunkt er objektet ikke blevet initialiseret endnu. Hvis din initialisering afsluttes med en undtagelse, vises finalize() metoden vil stadig blive kaldt. Det kan være fristende at løse dette ved objektets data, f.eks. indstille en initialized flag til true , men du kan kun sige dette for dine egne instansdata, men ikke for data fra en underklasse, som stadig ikke er blevet initialiseret, når din konstruktør vender tilbage.

    Registrering af en renser kræver en fuldt konstrueret Runnable opbevarer alle nødvendige data til oprydningen uden henvisning til objektet under opførelse. Du kan endda udskyde registreringen, når ressourceallokeringen ikke fandt sted i konstruktøren (tænk på en ubundet Socket instans eller en Frame som ikke er atommæssigt forbundet med en skærm)

  • En finalize() metode kan tilsidesættes, uden at kalde superklassemetoden eller undlade at gøre dette i det ekstraordinære tilfælde. Forhindrer metoden i at tilsidesætte ved at erklære den final , tillader slet ikke underklasserne at have sådanne oprydningshandlinger. I modsætning hertil kan hver klasse registrere rengøringsassistenter uden at forstyrre de andre rengøringsassistenter.

Indrømmet, du kunne have løst sådanne problemer med indkapslede objekter, men designet ved at have en finalize() metode for hver klasse guidet til den anden, forkerte retning.

  • Som du allerede har opdaget, er der en clean() metode, som gør det muligt at udføre oprydningshandlingen med det samme og fjerne renseren. Så når du giver en eksplicit lukkemetode eller endda implementerer AutoClosable , dette er den foretrukne måde at rydde op på, rettidig bortskaffelse af ressourcen og slippe af med alle problemerne med affaldsopsamlerbaseret oprydning.

    Bemærk, at dette harmonerer med punkterne nævnt ovenfor. Der kan være flere rengøringsmidler til en genstand, f.eks. registreret af forskellige klasser i hierarkiet. Hver af dem kan udløses individuelt med en iboende løsning vedrørende adgangsrettigheder, kun den, der har registreret renseren, får fat i den tilhørende Cleanable for at kunne kalde clean() metode.

Når det er sagt, overses det ofte, at det værste, der kan ske, når man styrer ressourcer med skraldeopsamleren, ikke er, at oprydningshandlingen kan køre senere eller aldrig overhovedet. Det værste, der kan ske, er, at det kører for tidligt . Se finalize() kaldet på stærkt tilgængeligt objekt i Java 8 for eksempel. Eller en rigtig fin en, JDK-8145304, Executors.newSingleThreadExecutor().submit(runnable) kaster RejectedExecutionException, hvor en færdiggører lukker den eksekveringstjeneste, der stadig er i brug.

Indrømmet, bare ved at bruge Cleaner eller PhantomReference løser ikke dette. Men at fjerne færdiggørelsesværktøjer og implementere en alternativ mekanisme, når det virkelig er nødvendigt, er en mulighed for nøje at tænke over emnet og måske indsætte reachabilityFence hvor det er nødvendigt. Det værste, du kan have, er en metode, der ser ud til at være nem at bruge, mens emnet i virkeligheden er forfærdeligt komplekst, og 99 % af dets brug potentielt går i stykker en dag.

Yderligere, mens alternativerne er mere komplekse, sagde du selv, er de sjældent nødvendige. Denne kompleksitet bør kun påvirke en brøkdel af din kodebase. Enhver hvorfor skulle java.lang.Object , basisklassen for alle klasser, er vært for en metode, der adresserer et sjældent hjørne tilfælde af Java-programmering?


Som påpeget af Elliott i kommentarer, gå videre med Java9+, Object.finalize er forældet, og derfor giver det mere mening at implementere metoder ved hjælp af Cleaner . Også fra udgivelsesbemærkningerne:

java.lang.Object.finalize metoden er blevet forældet. Finaliseringsmekanismen er i sagens natur problematisk og kan føre til problemer med topydelsen, dødvande og hænger. java.lang.ref.Cleaner og java.lang.ref.PhantomReference give mere fleksible og effektive måder at frigive ressourcer på, når et objekt bliver utilgængeligt.

Detaljer i fejldatabase - JDK-8165641


Java tag