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årObject()
konstruktør er blevet udført. På dette tidspunkt er objektet ikke blevet initialiseret endnu. Hvis din initialisering afsluttes med en undtagelse, visesfinalize()
metoden vil stadig blive kaldt. Det kan være fristende at løse dette ved objektets data, f.eks. indstille eninitialized
flag tiltrue
, 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 ubundetSocket
instans eller enFrame
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 denfinal
, 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 implementererAutoClosable
, 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 kaldeclean()
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