Perché non c'è distruzione di oggetti deterministici in Java? [duplicare]

4

Comprendo e apprezzo i vantaggi della Garbage Collection in Java. Tuttavia non capisco perché non ci sia modo in Java di distruggere in modo esplicito (e veloce) un oggetto. Sicuramente questo potrebbe essere utile in alcuni casi, presumo un software critico per le prestazioni.

È vero che in Java il GC cancellerà un oggetto senza alcun riferimento esistente ad esso, quindi se voglio un oggetto cancellato posso impostare il riferimento ad esso su null . Ma se ho capito bene, non è garantito che il GC cancellerà effettivamente l'oggetto, almeno non immediatamente. E questo è fuori dal controllo del programmatore.

Perché in Java non c'è modo di distruggere esplicitamente gli oggetti?

Pur comprendendo che Java è stato progettato per essere utilizzato come linguaggio di alto livello, questo astrae alcuni dettagli tecnici dal programmatore per semplificare le cose: Java è diventato uno dei linguaggi più utilizzati e viene utilizzato in enormi progetti. Suppongo che nei progetti enormi, le prestazioni siano spesso un problema. Dal momento che Java è cresciuto fino a diventare quello che è, perché non è stata aggiunta la distruzione dell'oggetto esplicita nel linguaggio?

    
posta Aviv Cohn 21.05.2014 - 23:09
fonte

5 risposte

7

Dobbiamo disaccoppiare due concetti qui.

A partire da Java 7, la lingua ha RAII . Si chiama dichiarazione try-with-resources .

static String readFirstLineFromFile(String path) throws IOException {
  try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
  }
}

Ciò garantisce che il lettore venga chiuso indipendentemente dal modo in cui il blocco si chiude.

Anche senza usare try-with-resources, puoi chiudere in modo deterministico uno stream chiamando br.close() quando vuoi.

Ciò che non è deterministico è l'eliminazione dell'oggetto dalla memoria. Quello deve aspettare fino a che non ci sono più riferimenti all'oggetto e il garbage collector lo nota. Cercando di rendere tale deterministico dipende potenzialmente un sacco di programma (ad esempio un listener, il debugger o un logger fa ancora riferimento all'oggetto?) E i dettagli operativi del garbage collector (che è l'algoritmo GC in uso? È un collector concorrente ? quale generazione è l'oggetto in? quanto urgente è la raccolta necessaria in questo momento? ...).

    
risposta data 22.05.2014 - 03:45
fonte
5

Ho spesso riflettuto sulla stessa domanda.

Sono stato un programmatore C ++ per molti anni e un programmatore Objective C per molti anni prima. Quell'esperienza mi ha insegnato la disciplina per tracciare ogni creazione di oggetti e ottenere il nirvana del codice - per assicurare logicamente la distruzione finale di ogni oggetto creato e buffer .

E che disciplina era - potrei garantire che non ho mai perso memoria nel mio codice. Ma la memoria è trapelata! Qualsiasi biblioteca che ho usato era una crepa in questa armatura. Threading era un incubo !! La disciplina era preziosa ma non risolveva il problema. Anche il codice più attentamente scritto richiederebbe un riavvio dopo alcune settimane di funzionamento continuo e molte app non potrebbero funzionare stabilmente per più di 24 ore.

La soluzione è Garbage Collection. Rende tutto così semplice - basta lasciare andare la memoria e galleggia come un pallone pieno di elio da raccogliere e riciclare.

Ci sono due principali vantaggi che lo rendono un vincitore:

  1. È semplice: lascia andare l'oggetto.
  2. Il processo GC compatta anche il tuo heap.

Ora quei due creano un miglioramento nell'ordine di grandezza nella stabilità del codice e nella facilità di scrittura e debug. L'unico lato negativo è che devi subire l'umiliazione di sopportare di tanto in tanto l'inevitabile ritardo del GC.

Una rapida considerazione di consentire una mescolanza di scarti immediati e posticipati si dimostra inutile.

Supponiamo di avere un Map che hai riempito con alcuni dati e ora sai senza dubbio che non sarà più necessario. Sicuramente la sua liberazione manuale ti farebbe risparmiare noccioline - solo i nodi, non le chiavi oi valori. Un piccolo guadagno di alcuni valori frammentati.

Conclusione: è così semplice che persino un programmatore incompetente può scrivere codice senza perdite.

    
risposta data 22.05.2014 - 00:09
fonte
3

Uscita da qui , che elenca gli obiettivi di progettazione del linguaggio di programmazione Java (enfasi mio):

1.2.2 Robust and Secure

The Java programming language is designed for creating highly reliable software. It provides extensive compile-time checking, followed by a second level of run-time checking. Language features guide programmers towards reliable programming habits. The memory management model is extremely simple: objects are created with a new operator. There are no explicit programmer-defined pointer data types, no pointer arithmetic, and automatic garbage collection. This simple memory management model eliminates entire classes of programming errors that bedevil C and C++ programmers. You can develop Java code with confidence that the system will find many errors quickly and that major problems won't lay dormant until after your production code has shipped.

La parte in grassetto, penso, risponde alla tua domanda originale abbastanza bene.

Per quanto riguarda la seconda parte della tua domanda, non credo di poterlo rispondere personalmente a sufficienza. Tuttavia, googling mi ha portato a questa risposta di Jon Skeet su Stack Overflow. Egli menziona che i distruttori deterministici tendono a coinvolgere il conteggio dei riferimenti, che influisce sulle prestazioni e fallisce per i cicli, e rimanda i lettori a questa email di Brain Harry sulla gestione delle risorse. Non riesco a verificare se queste risposte siano vere, ma entrambe indicano che i distruttori deterministici non meritano di essere implementati, poiché i loro difetti superano i potenziali benefici dell'implementazione.

(A proposito, penso che le fonti collegate forniscano troppe informazioni per essere ragionevolmente citate qui. Se dovessi citarle comunque, fammi sapere)

    
risposta data 21.05.2014 - 23:29
fonte
3

Il GC tenta di emulare un mondo in cui non esaurisce mai la memoria , e questo è da dove provengono i vantaggi in termini di velocità e manutenibilità.

In teoria, il garbage collector dovrebbe sapere di più se ora è un buon momento per distruggere gli oggetti rispetto a quando lo programmerai. Spesso programmi in lingue con meno memoria di astrazione il prima possibile. Tuttavia, non è sempre buono per le prestazioni. Quindi è possibile liberare memoria in un secondo momento, in cui il luogo in cui gli oggetti vengono allocati e distrutti è molto lontano. Questo aumenta il potenziale che il programmatore perderà qualcosa. Quindi può essere un male per l'utilizzo della memoria, dato che le cose dovrebbero essere distrutte quando il sistema ottiene un po 'di respiro piuttosto che nel mezzo di un loop ad alta velocità. Ma può essere più veloce in questo modo.

Se il garbage collector è in grado di girare in un thread separato e fare il suo lavoro senza corrompere la memoria del thread principale, allora sarà più veloce che se il thread principale gestirà la sua memoria manualmente .

In pratica, dipende da molte cose diverse. Il processo può essere multithread, quanta memoria è disponibile (meno significa più raccolte, più significa velocità più veloce), ecc. Anche se è possibile forzare la garbage collection, questa è una funzionalità molto spesso abusata. può ripulire quegli oggetti, ma molte volte ci sono riferimenti che il programmatore non ha realizzato che stavano ancora usando. Distruggere così con la forza quegli oggetti potrebbe creare problemi che non diventerebbero evidenti solo molto più tardi, in cui il GC sa che quegli oggetti sono ancora in uso.

Combinato con riciclaggio , in modo che la memoria per gli oggetti non debba essere riallocata se non necessario, puoi ottenere elaborazione molto più veloce con molto meno lavoro e manutenzione molto migliore.

    
risposta data 21.05.2014 - 23:58
fonte
-1

Java esegue la pulizia automatica:

Come tutti sappiamo, Garbage Collector fa automaticamente il lavoro di pulizia, ma QUANDO? Ogni oggetto è idoneo per la pulizia quando non ha collegamenti.

Car c1=new Car();---Object 1
c1=new Car(); ---Object 2

Nella riga 2, l'oggetto 1 è idoneo per la garbage collection, ma questo potrebbe essere raccolto o meno, dipende da molti altri fattori. Ci sono tre spazi di heap Eden, Survivor, Tenured Generation e JVM vuole sempre un corretto bilanciamento della memoria (un grafico corretto del dente di sega). Ogni oggetto nato nello spazio dell'Eden e segue altri spazi di heap in base alla sua età in programma. Quindi, quando uno di questi spazi heap si riempie, GC cerca gli oggetti senza collegamenti e effettua la pulizia per tali oggetti e sposta l'oggetto rimanente nello spazio heap successivo.

Ecco perché in JDBC se non chiudiamo Connections, Resultsets, Statements c'è una condizione di OutOfMemory .

Quindi JVM segue questo tipo di gestione della pulizia che ha avuto successo nella maggior parte dei casi e non c'è bisogno di una gestione personalizzata della memoria. Anche per prevenire casi come JDBC, il problema di outofmory, java introduce delle risorse, che liberano l'oggetto alla fine del campo di prova.

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
  }

Non confondere con il metodo finalize () che chiama solo prima di invocare la raccolta di rifiuti, non ha alcuna connessione alla memoria personalizzata pulita come i distruttori in C ++.

    
risposta data 23.05.2014 - 08:27
fonte

Leggi altre domande sui tag