Is er een destructor voor Java?

Is er een destructor voor Java? Ik lijk hier geen documentatie over te kunnen vinden. Als dat niet het geval is, hoe kan ik dan hetzelfde effect bereiken?

Om mijn vraag specifieker te maken, ben ik een applicatie aan het schrijven die zich bezighoudt met gegevens en in de specificatie staat dat er een ‘reset’-knop zou moeten zijn die de applicatie terugbrengt naar de oorspronkelijke, net gestarte staat. Alle gegevens moeten echter ‘live’ zijn, tenzij de applicatie wordt gesloten of de reset-knop wordt ingedrukt.

Omdat ik meestal een C/C++-programmeur ben, dacht ik dat dit triviaal zou zijn om te implementeren. (En daarom was ik van plan om het als laatste te implementeren.) Ik heb mijn programma zo gestructureerd dat alle ‘reset-able’ objecten in dezelfde klasse zouden zijn, zodat ik gewoon alle ‘live’ objecten kan vernietigen wanneer een reset-knop wordt ingedrukt.

Ik dacht dat als ik alleen maar de verwijzing naar de gegevens deed en wacht tot de vuilnisman ze ophaalt, er dan geen geheugenlek zou zijn als mijn gebruiker herhaaldelijk gegevens zou invoeren en op de resetknop zou drukken? Ik zat ook te denken, aangezien Java behoorlijk volwassen is als taal, er een manier zou moeten zijn om dit te voorkomen of om dit op een elegante manier aan te pakken.


Antwoord 1, autoriteit 100%

Omdat Java een door afval verzamelde taal is, kun je niet voorspellen wanneer (of zelfs of) een object zal worden vernietigd. Er is dus geen direct equivalent van een destructor.

Er is een overgeërfde methode genaamd finalize, maar dit is geheel naar goeddunken van de vuilnisman. Dus voor klassen die expliciet moeten worden opgeruimd, is de conventie om een close-methode te definiëren en finalize alleen te gebruiken voor sanity-controle (dwz als closeniet is aangeroepen, doe het dan nu en log een fout in).

Er was een vraag die onlangs aanleiding gaf tot een diepgaande discussie over finalize, dus dat zou meer diepte moeten geven indien nodig…


Antwoord 2, autoriteit 24%

Bekijk de try-with-resourcesuitspraak. Bijvoorbeeld:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  System.out.println(br.readLine());
} catch (Exception e) {
  ...
} finally {
  ...
}

Hier wordt de bron die niet langer nodig is, vrijgemaakt in de methode BufferedReader.close(). U kunt uw eigen klasse maken die AutoCloseableimplementeert en deze op een vergelijkbare manier gebruiken.

Deze verklaring is beperkter dan finalizein termen van codestructurering, maar maakt tegelijkertijd de code eenvoudiger te begrijpen en te onderhouden. Er is ook geen garantie dat een finalize-methode wordt aangeroepen tijdens de livetime van de toepassing.


Antwoord 3, autoriteit 20%

Nee, geen vernietigers hier. De reden is dat alle Java-objecten worden toegewezen aan een heap en dat het afval wordt verzameld. Zonder expliciete deallocatie (d.w.z. C++’s delete-operator) is er geen verstandige manier om echte destructors te implementeren.

JAVA ondersteunt de finalisatoren, maar ze zijn bedoeld om alleen te worden gebruikt als een waarborg voor objecten die een handgreep houden aan native resources zoals sockets, bestandshandvatten, vensterhandvatten, enz. Wanneer de vuilnisbak een object verzamelt zonder een doelpunt markeert de geheugenregio als gratis en dat is het. Wanneer het object een finalizer heeft, wordt het voor het eerst gekopieerd naar een tijdelijke locatie (vergeet niet dat we hier zijn), dan is het ingeschakeld in een wachtrij-rij-wachtrij en vervolgens een finalizer-thread peilt de wachtrij met een zeer lage prioriteit en voert de finalizer uit.

Wanneer de toepassing van de toepassing wordt uitgesloten, stopt de JVM zonder te wachten op de in afwachting van objecten die moeten worden afgerond, dus er is praktisch geen garanties dat uw finalisatoren ooit zullen uitvoeren.


Antwoord 4, Autoriteit 6%

Gebruik van Finalize () Methoden moeten worden vermeden. Ze zijn geen betrouwbaar mechanisme voor het opruimen van hulpbronnen en het is mogelijk om problemen in de vuilniscollector te veroorzaken door ze te misbruiken.

Als u een deallocatie-oproep in uw object nodig heeft, zeg dan om bronnen vrij te geven, een expliciete methode-oproep vrij te geven. Deze conventie is te zien in bestaande API’s (bijv. Slaapbaar , graphics.dispose ( ) , Widget.Dis Pose () ) en wordt meestal via Try / Eindelijk gebeld.

Resource r = new Resource();
try {
    //work
} finally {
    r.dispose();
}

Pogingen om een verwijderd object te gebruiken zouden een runtime-uitzondering moeten veroorzaken (zie IllegalStateException).


BEWERKEN:

Ik zat te denken, als alles wat ik deed was gewoon
om de referentie van de gegevens te verwijderen en te wachten op:
de vuilnisman om ze op te halen,
zou er geen geheugenlek zijn als mijn
gebruiker herhaaldelijk ingevoerde gegevens en
op de resetknop gedrukt?

Over het algemeen hoef je alleen maar de verwijzingen naar de objecten te verwijderen – dit is tenminste de manier waarop het zou moeten werken. Als je je zorgen maakt over het verzamelen van afval, ga dan naar Java SE 6 HotSpot[tm] Vuilnisinzameling afstemmen op virtuele machine(of het equivalente document voor uw JVM-versie).


Antwoord 5, autoriteit 4%

Nu Java 1.7 is uitgebracht, heb je nu de extra optie om het blok try-with-resourceste gebruiken. Bijvoorbeeld

public class Closeable implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("closing..."); 
    }
    public static void main(String[] args) {
        try (Closeable c = new Closeable()) {
            System.out.println("trying..."); 
            throw new Exception("throwing..."); 
        }
        catch (Exception e) {
            System.out.println("catching..."); 
        }
        finally {
            System.out.println("finalizing..."); 
        } 
    }
}

Als u deze klasse uitvoert, wordt c.close()uitgevoerd wanneer het blok trywordt verlaten, en vóór de catchen finallyblokken worden uitgevoerd. Anders dan in het geval van de finalize()methode, wordt close()gegarandeerd uitgevoerd. Het is echter niet nodig om het expliciet uit te voeren in de finally-clausule.


Antwoord 6, autoriteit 3%

Ik ga volledig akkoord met andere antwoorden en zeg niet te vertrouwen op de uitvoering van finalize.

Naast try-catch-finally-blokkeringen, kunt u Runtime#addShutdownHook(geïntroduceerd in Java 1.3) om de laatste opschoning van uw programma uit te voeren.

Dat is niet hetzelfde als destructors, maar men kan een shutdown-hook implementeren met listener-objecten geregistreerd waarop opschoningsmethoden (permanente databaseverbindingen sluiten, bestandsvergrendelingen verwijderen, enzovoort) worden aangeroepen – dingen die normaal gesproken zouden worden gedaan in destructors.
Nogmaals – dit is geen vervanging voor destructors, maar in sommige gevallen kunt u hiermee de gewenste functionaliteit benaderen.

Het voordeel hiervan is dat het deconstructiegedrag losjes gekoppeldis met de rest van je programma.


Antwoord 7, autoriteit 2%

Nee, java.lang.Object#finalizekomt het dichtst in de buurt.

Wanneer (en of) het wordt aangeroepen, is echter niet gegarandeerd.
Zie: java.lang.Runtime#runFinalizersOnExit(boolean)


Antwoord 8

Ten eerste, houd er rekening mee dat, aangezien Java door vuilnis wordt verzameld, het zelden nodig is om iets aan objectvernietiging te doen. Ten eerste omdat je meestal geen beheerde middelen hebt om vrij te maken, en ten tweede omdat je niet kunt voorspellen wanneer en of het zal gebeuren, dus het is ongepast voor dingen die je moet doen “zodra niemand mijn object meer gebruikt “.

U kunt een melding krijgen nadat een object is vernietigd met behulp van java.lang.ref.PhantomReference (eigenlijk is zeggen dat het is vernietigd enigszins onnauwkeurig, maar als een fantoomverwijzing ernaar in de wachtrij staat, kan het niet meer worden hersteld, wat komt meestal op hetzelfde neer). Een veelgebruikt gebruik is:

  • Scheid de resource(s) in uw klas die moeten worden vernietigd in een ander helper-object (merk op dat als u alleen maar een verbinding sluit, wat vaak voorkomt, u geen nieuwe klasse: de te sluiten verbinding zou in dat geval het “helperobject” zijn).
  • Als je je hoofdobject maakt, maak er dan ook een PhantomReference naar aan. Ofwel laat dit verwijzen naar het nieuwe helper-object, of stel een kaart in van PhantomReference-objecten naar hun corresponderende helper-objecten.
  • Nadat het hoofdobject is verzameld, wordt de PhantomReference in de wachtrij geplaatst (of liever gezegd, het kan in de wachtrij worden geplaatst – net als finalizers is er geen garantie dat dit ooit zal gebeuren, bijvoorbeeld als de VM wordt afgesloten, wacht hij niet). Zorg ervoor dat je de wachtrij verwerkt (in een speciale thread of van tijd tot tijd). Vanwege de harde verwijzing naar het helper-object is het helper-object nog niet verzameld. Dus doe wat je maar wilt op het helper-object, gooi vervolgens de PhantomReference weg en de helper zal uiteindelijk ook worden verzameld.

Er is ook finalize(), dat eruitziet als een destructor, maar zich niet zo gedraagt. Het is meestal geen goede optie.


Antwoord 9

De functie finalize()is de destructor.

Het zou echter normaal niet moeten worden gebruikt omdat het na de GCwordt aangeroepen en je niet kunt zeggen wanneer dat zal gebeuren (of ooit).

Bovendien is er meer dan één GC nodig om de toewijzing van objecten met finalize()ongedaan te maken.

Probeer op te schonen op de logische plaatsen in uw code met behulp van de try{...} finally{...}-instructies!


Antwoord 10

Ik ben het eens met de meeste antwoorden.

U moet niet volledig afhankelijk zijn van finalizeof ShutdownHook

afsluiten

  1. De JVM kan niet garanderen wanneer deze finalize()-methode wordt aangeroepen.

  2. finalize()wordt slechts één keer aangeroepen door GC-thread. Als een object zichzelf herstelt van de finaliseringsmethode, wordt finalizeniet opnieuw aangeroepen.

  3. In uw toepassing kunt u enkele live objecten hebben, waarop de garbagecollection nooit wordt aangeroepen.

  4. Elke Exceptiondie wordt gegenereerd door de finaliseringsmethode wordt genegeerd door de GC-thread

  5. System.runFinalization(true)en Runtime.getRuntime().runFinalization(true)methoden vergroten de kans op het aanroepen van finalize()methode, maar nu zijn deze twee methoden verouderd. Deze methoden zijn erg gevaarlijk vanwege een gebrek aan threadveiligheid en het mogelijk maken van een deadlock.

shutdownHooks

public void addShutdownHook(Thread hook)

Registreert een nieuwe afsluithaak voor virtuele machines.

De virtuele Java-machine wordt afgesloten als reactie op twee soorten gebeurtenissen:

  1. Het programma wordt normaal afgesloten wanneer de laatste niet-daemon-thread wordt afgesloten of wanneer de exit-methode (equivalent, System.exit) wordt aangeroepen, of

  2. De virtuele machine wordt beëindigd als reactie op een gebruikersonderbreking, zoals het typen van ^C, of een systeembrede gebeurtenis, zoals het afmelden van een gebruiker of het afsluiten van het systeem.

  3. Een shutdown-hook is gewoon een geïnitialiseerde maar niet-gestarte thread. Wanneer de virtuele machine zijn afsluitvolgorde begint, zal het alle geregistreerde afsluithaken in een niet-gespecificeerde volgorde starten en ze gelijktijdig laten draaien. Wanneer alle hooks zijn voltooid, worden alle niet-aangeroepen finalizers uitgevoerd als finalization-on-exit is ingeschakeld.

  4. Eindelijk stopt de virtuele machine. Houd er rekening mee dat daemon-threads blijven draaien tijdens het afsluiten, net als niet-daemon-threads als het afsluiten werd gestart door de exit-methode aan te roepen.

  5. Shutdown-haken moeten hun werk ook snel afmaken. Wanneer een programma exit aanroept, is de verwachting dat de virtuele machine onmiddellijk wordt afgesloten en afgesloten.

    Maar zelfs Oracle-documentatie citeerde dat

In zeldzame gevallen kan de virtuele machine worden afgebroken, dat wil zeggen, stoppen met draaien zonder netjes af te sluiten

Dit gebeurt wanneer de virtuele machine extern wordt beëindigd, bijvoorbeeld met het SIGKILL-signaal op Unix of de TerminateProcess-aanroep op Microsoft Windows. De virtuele machine kan ook worden afgebroken als een native methode misgaat door bijvoorbeeld interne gegevensstructuren te beschadigen of toegang te krijgen tot niet-bestaand geheugen. Als de virtuele machine wordt afgebroken, kan er geen garantie worden gegeven of er wel of geen shutdown-hooks zullen worden uitgevoerd.

Conclusie: gebruik try{} catch{} finally{}blokken op de juiste manier en geef kritieke bronnen vrij in finally(}block. Tijdens het vrijgeven van resources in finally{}block, vang Exceptionen Throwable.


Antwoord 11

Als u zich alleen zorgen maakt over het geheugen, niet doen. Vertrouw gewoon op de GC, hij doet goed werk. Ik zag iets over het feit dat het zo efficiënt is dat het voor de prestaties beter zou kunnen zijn om hopen kleine objecten te maken dan om in sommige gevallen grote arrays te gebruiken.


Antwoord 12

Misschien kunt u een try …final block gebruiken om het object af te ronden in de besturingsstroom waarmee u het object gebruikt. Natuurlijk gebeurt het niet automatisch, maar vernietiging in C++ ook niet. Je ziet vaak het sluiten van bronnen in het slotblok.


Antwoord 13

Er is een @Cleanupannotatie in Lombok die het meest lijkt op C++ destructors:

@Cleanup
ResourceClass resource = new ResourceClass();

Bij het verwerken ervan (op compilatietijd), voegt Lombok de juiste try-finallyblok toe, zodat resource.close()wordt aangeroepen, wanneer uitvoering de reikwijdte van de variabele. U kunt ook expliciet een andere methode specificeren voor het vrijgeven van de resource, b.v. resource.dispose():

@Cleanup("dispose")
ResourceClass resource = new ResourceClass();

Antwoord 14

Het dichtstbijzijnde equivalent aan een destructor in Java is de Finalize () methode. Het grote verschil voor een traditionele destructor is dat je niet zeker weet wanneer het wordt gebeld, want dat is de verantwoordelijkheid van de vuilniscollector. Ik zou u ten zeerste zorgvuldig aanraden om dit vast te lezen voordat ik het gebruikt, aangezien uw typische RAIA-patronen voor bestandshandvatten enzovoort, niet betrouwbaar zullen werken met finaliseren ().


Antwoord 15

Neer nadenken over de oorspronkelijke vraag … wat, ik denk dat we kunnen concluderen uit alle andere geleerde antwoorden, en ook van Bloch’s essentiële effectieve Java , Item 7, “Vermijd finalizers”, zoekt de oplossing voor een legitieme vraag op een manier die niet geschikt is voor de Java-taal …:

… Zou een vrij duidelijke oplossing niet doen wat de OP daadwerkelijk wil zijn om al uw objecten te houden die moeten worden gereset in een soort van “boxen”, waarop alle andere niet-resetteerbare objecten alleen referenties hebben via een soort accessorobject …

En wanneer u vervolgens wilt “resetten”, koppelt u de bestaande boxen los en maakt u een nieuwe: al het web van objecten in de box is gegoten op drift, nooit om terug te keren, en op een dag te worden verzameld door de GC.

Als een van deze objecten Closeable(of niet, maar een closemethode) kunt u ze in een Bagin de Kinderboxen zoals ze zijn gemaakt (en mogelijk geopend), en de laatste daad van de accessor voordat het afsnijden van de boxen zou zijn om door alle Closeableste sluiten …?

De code lijkt waarschijnlijk zoiets uit:

accessor.getPlaypen().closeCloseables();
accessor.setPlaypen( new Playpen() );

closeCloseableszou waarschijnlijk een blokkeermethode zijn, waarschijnlijk een vergrendeling (bijv. CountdownLatch), om met (en wacht even nodig voor) elke Runnables/ CallablesIn alle discussies die specifiek zijn voor de Playpenom als van toepassing te worden beëindigd, met name in de Javafx-thread.


Antwoord 16

Veel geweldige antwoorden hier, maar er is wat aanvullende informatie over waarom u moet vermijden met behulp van finaliseren () .

Als de JVM uitgaat dankzij System.exit()of Runtime.getRuntime().exit(), finalisatoren worden standaard niet uitgevoerd. Van javadoc voor runtime.Exit ( ) :

De afsluitvolgorde van de virtuele machine bestaat uit twee fasen. In de eerste fase worden alle geregistreerde shutdown-hooks, indien aanwezig, in een niet-gespecificeerde volgorde gestart en mogen ze gelijktijdig worden uitgevoerd totdat ze zijn voltooid. In de tweede fase worden alle niet-aangeroepen finalizers uitgevoerd als finalization-on-exit is ingeschakeld. Zodra dit is gebeurd, stopt de virtuele machine.

Je kunt System.runFinalization()maar het doet alleen “een uiterste best om alle openstaande afrondingen te voltooien” – geen garantie.

Er is een System.runFinalizersOnExit()methode, maar gebruik het niet – het is onveilig, lang geleden verouderd.


Antwoord 17

Als u een Java-applet schrijft, kunt u de Applet-methode “destroy()” overschrijven. Het is…

* Called by the browser or applet viewer to inform
 * this applet that it is being reclaimed and that it should destroy
 * any resources that it has allocated. The stop() method
 * will always be called before destroy().

Natuurlijk niet wat jewilt, maar misschien zijn andere mensen daar naar op zoek.


Antwoord 18

Hoewel er aanzienlijke vooruitgang is geboekt in de GC-technologie van Java, moet u toch rekening houden met uw referenties. Talloze gevallen van schijnbaar triviale referentiepatronen die in feite rattennesten onder de motorkap zijn, komen voor de geest.

Uit je bericht lijkt het niet alsof je een resetmethode probeert te implementeren voor hergebruik van objecten (waar?). Bevatten uw objecten andere soorten bronnen die moeten worden opgeruimd (d.w.z. streams die moeten worden gesloten, gepoolde of geleende objecten die moeten worden geretourneerd)? Als het enige waar je je zorgen over maakt, is geheugendealloc, dan zou ik mijn objectstructuur heroverwegen en proberen te verifiëren dat mijn objecten op zichzelf staande structuren zijn die op GC-tijd zullen worden opgeruimd.


Antwoord 19

Nee Java heeft geen destructors. De belangrijkste reden in Java zijn de Garbage Collectors die altijd passief op de achtergrond werken en alle objecten worden gemaakt in het heapgeheugen, dat is de plaats waar GC werkt. c++ daar moeten we expliciet de delete-functie aanroepen, omdat er geen Garbage Collector-achtig ding is.


Antwoord 20

In Java verwijdert de vuilnisman automatisch de ongebruikte objecten om geheugen vrij te maken. Het is dus logisch dat Java geen destructors beschikbaar heeft.


Antwoord 21

Er is geen exacte destructorklasse in Java, klasse wordt automatisch vernietigd in Java door garbage collector . maar je zou dat kunnen doen met behulp van onderstaande, maar het is niet precies hetzelfde:

afsluiten()

Er was een vraag die aanleiding gaf tot een diepgaande discussie over finalize, zodat u indien nodig meer diepgang zou moeten krijgen…


Antwoord 22

Vroeger hield ik me voornamelijk bezig met C++ en dat bracht me ook op het zoeken naar een destructor. Ik gebruik JAVA nu veel. Wat ik deed, en het is misschien niet het beste geval voor iedereen, maar ik heb mijn eigen destructor geïmplementeerd door alle waarden opnieuw in te stellen op 0 of hun standaardwaarde via een functie.

Voorbeeld:

public myDestructor() {
variableA = 0; //INT
variableB = 0.0; //DOUBLE & FLOAT
variableC = "NO NAME ENTERED"; //TEXT & STRING
variableD = false; //BOOL
}

In het ideale geval werkt dit niet voor alle situaties, maar als er globale variabelen zijn, werkt het zolang je er niet een heleboel hebt.

Ik weet dat ik niet de beste Java-programmeur ben, maar het lijkt voor mij te werken.

Other episodes