Perché linguaggi come C e C ++ non dispongono della garbage collection, mentre Java lo fa? [chiuso]

54

Bene, so che ci sono cose come malloc / free per C e nuovo / using-a-destructor per la gestione della memoria in C ++, ma mi chiedevo perché non ci sono "nuovi aggiornamenti" in questi linguaggi che permettono l'utente ha la possibilità di gestire manualmente la memoria, o che il sistema lo faccia automaticamente (garbage collection)?

Un po 'una domanda newb-ish, ma solo in CS da circa un anno.

    
posta Dark Templar 14.10.2011 - 21:41
fonte

16 risposte

68

La raccolta dei dati inutili richiede strutture di dati per il tracciamento delle allocazioni e / o il conteggio dei riferimenti. Questi creano un sovraccarico di memoria, prestazioni e complessità del linguaggio. C ++ è progettato per essere "vicino al metallo", in altre parole, ci vuole il lato più performante del tradeoff rispetto alle caratteristiche di convenienza. Altre lingue rendono questo compromesso in modo diverso. Questa è una delle considerazioni nella scelta di una lingua, quale enfasi preferisci.

Detto questo, ci sono molti schemi per il conteggio dei riferimenti in C ++ che sono abbastanza leggeri e performanti, ma sono nelle librerie, sia commerciali che open source, piuttosto che parte del linguaggio stesso. Il conteggio dei riferimenti per gestire la durata dell'oggetto non è lo stesso della garbage collection, ma affronta molti degli stessi tipi di problemi ed è più adatto all'approccio di base di C ++.

    
risposta data 06.09.2015 - 06:55
fonte
43

In senso stretto, non esiste alcuna gestione della memoria nel linguaggio C. malloc () e free () non sono parole chiave nella lingua, ma solo funzioni chiamate da una libreria. Questa distinzione può essere pedante ora, perché malloc () e free () fanno parte della libreria standard C e saranno forniti da qualsiasi implementazione standard compatibile di C, ma questo non era sempre vero in passato.

Perché vorresti un linguaggio senza standard per la gestione della memoria? Questo risale alle origini di C come "assemblaggio portatile". Esistono molti casi di hardware e algoritmi che possono trarre beneficio o persino richiedere tecniche specializzate di gestione della memoria. Per quanto ne so, non c'è modo di disabilitare completamente la gestione della memoria nativa di Java e sostituirla con la tua. Questo semplicemente non è accettabile in alcune situazioni di alte prestazioni / risorse minime. C offre una flessibilità quasi completa per scegliere esattamente quale infrastruttura verrà utilizzata dal programma. Il prezzo pagato è che il linguaggio C fornisce pochissimo aiuto nella scrittura di un codice corretto e privo di bug.

    
risposta data 09.10.2011 - 00:12
fonte
31

La vera risposta è che l'unico modo per rendere sicuro ed efficiente il meccanismo di garbage collection è di avere supporto a livello di linguaggio per riferimenti opachi. (O, al contrario, una mancanza di supporto a livello di linguaggio per la manipolazione diretta della memoria.)

Java e C # possono farlo perché hanno tipi di riferimento speciali che non possono essere manipolati. Questo dà al runtime la libertà di fare cose come spostare gli oggetti allocati nella memoria , che è fondamentale per un'implementazione GC ad alte prestazioni.

Per la cronologia, nessuna implementazione GC moderna utilizza il conteggio dei riferimenti , quindi è completamente un'aringa rossa. I GC moderni utilizzano la raccolta generazionale, dove le nuove allocazioni sono trattate essenzialmente allo stesso modo in cui le allocazioni dello stack sono in un linguaggio come C ++, e periodicamente tutti gli oggetti allocati di recente che sono ancora vivi vengono spostati in uno spazio "sopravvissuto" separato e un'intera generazione di oggetti è deallocato in una volta.

Questo approccio ha vantaggi e svantaggi: l'aspetto positivo è che le allocazioni dell'heap in una lingua che supporta GC sono veloci quanto le allocazioni dello stack in una lingua che non supporta GC, e lo svantaggio è che gli oggetti che devono essere ripuliti prima di essere distrutti richiedono un meccanismo separato (ad es. la parola chiave using di C #), altrimenti il loro codice di pulizia viene eseguito in modo non deterministico.

Si noti che una chiave per un GC ad alte prestazioni è che deve esistere un supporto linguistico per una classe speciale di riferimenti. C non ha questo supporto linguistico e non lo farà mai; poiché il C ++ ha un sovraccarico dell'operatore, potrebbe emulare un tipo di puntatore GC, anche se dovrebbe essere fatto con attenzione. Infatti, quando Microsoft ha inventato il loro dialetto di C ++ che sarebbe eseguito sotto CLR (il runtime .NET), è stato necessario inventare una nuova sintassi per "riferimenti in stile C #" (ad esempio Foo^ ) per distinguerli da "C ++ - riferimenti di stile "(ad es. Foo& ).

Ciò che C ++ ha, e ciò che viene regolarmente utilizzato dai programmatori C ++, è puntatori intelligenti , che sono in realtà solo un meccanismo di conteggio dei riferimenti. Non considererei il conteggio dei riferimenti come GC "vero", ma offre molti degli stessi vantaggi, a costo di prestazioni più lente rispetto alla gestione manuale della memoria o al vero GC, ma con il vantaggio della distruzione deterministica.

Alla fine della giornata, la risposta si riduce a una funzionalità di progettazione del linguaggio. C ha fatto una scelta, il C ++ ha fatto una scelta che gli ha permesso di essere retrocompatibile con C pur fornendo alternative che sono abbastanza buone per la maggior parte degli scopi, e Java e C # hanno fatto una scelta diversa che è incompatibile con C ma è anche abbastanza buona per la maggior parte degli scopi. Sfortunatamente, non esiste un proiettile argentato, ma conoscere le diverse opzioni disponibili ti aiuterà a scegliere quello giusto per qualsiasi programma tu stia attualmente cercando di costruire.

    
risposta data 07.09.2015 - 01:02
fonte
26

Perché, quando si utilizza la potenza del C ++, non ce n'è bisogno.

Herb Sutter: " Non ho scritto cancella da anni. "

vedi Scrivendo codice C ++ moderno: come C ++ si è evoluto negli anni 21:10

Potrebbe sorprendere molti programmatori C ++ esperti.

    
risposta data 08.10.2011 - 23:27
fonte
15

"Tutti" un garbage collector è un processo che viene eseguito periodicamente per verificare se nella memoria ci sono oggetti non referenziati e se li elimina. (Sì, so che questa è una grossolana semplificazione). Questa non è una proprietà del linguaggio, ma il framework.

Ci sono garbage collector scritti per C e C ++ - questo per esempio.

Uno dei motivi per cui uno non è stato "aggiunto" al linguaggio potrebbe essere a causa del volume puro del codice esistente che non lo userebbe mai poiché utilizzano il proprio codice per la gestione della memoria. Un altro motivo potrebbe essere che i tipi di applicazioni scritte in C e C ++ non richiedono l'overhead associato a un processo di garbage collection.

    
risposta data 08.10.2011 - 23:09
fonte
12

C è stato progettato in un'epoca in cui la raccolta dei rifiuti era a malapena un'opzione. Era anche inteso per usi in cui la garbage collection non funzionava in generale: bare metal, ambienti in tempo reale con memoria minima e supporto di runtime minimo. Ricorda che C era il linguaggio di implementazione del primo unix, che girava su un pdp-11 con 64 * K * byte di memoria. C ++ era originariamente un'estensione di C - la scelta era già stata fatta, ed è molto difficile innestare la garbage collection su una lingua esistente. È il tipo di cosa che deve essere costruita dal piano terra.

    
risposta data 09.10.2011 - 00:19
fonte
9

Non ho le virgolette esatte ma sia Bjarne che Herb Sutter dicono qualcosa in questo modo:

C++ doesn't need a garbage collector, because it has no garbage.

Nel moderno C ++ utilizzi puntatori intelligenti e quindi non hai spazzatura.

    
risposta data 08.10.2011 - 22:56
fonte
8

Chiedete perché queste lingue non sono state aggiornate per includere un garbage collector facoltativo.

Il problema con la garbage collection facoltativa è che non è possibile combinare il codice che utilizza i diversi modelli. Cioè, se scrivo codice che presume che tu stia usando un garbage collector non puoi usarlo nel tuo programma che ha la garbage collection disattivata. Se lo fai, si diffonderà ovunque.

    
risposta data 09.10.2011 - 06:53
fonte
6

Riesci a immaginare di scrivere un gestore di dispositivi in una lingua con la garbage collection? Quanti bit potrebbero arrivare lungo la linea mentre il GC era in esecuzione?

O un sistema operativo? Come hai potuto avviare la garbage collection in esecuzione prima ancora di avviare il kernel?

C è progettato per un livello basso vicino alle attività hardware. Il problema? è un linguaggio così bello che è una buona scelta anche per molti compiti di livello superiore. Gli zar della lingua sono a conoscenza di questi usi, ma hanno bisogno di supportare i requisiti dei driver di dispositivo, del codice incorporato e dei sistemi operativi come priorità.

    
risposta data 14.10.2011 - 10:42
fonte
6

La risposta breve e noiosa a questa domanda è che per le persone che scrivono i garbage collector deve esserci un linguaggio non-garbage collection. Non è concettualmente facile avere un linguaggio che, allo stesso tempo, consente un controllo molto preciso sul layout di memoria e ha un GC in esecuzione sopra.

L'altra domanda è perché C e C ++ non hanno garbage collector. Beh, so che il C ++ ne ha un paio in giro ma non sono molto popolari perché sono costretti a gestire un linguaggio che non è stato progettato per essere GC-ed, in primo luogo, e le persone che usano ancora il C ++ in questa età non è proprio il tipo che manca a un GC.

Inoltre, invece di aggiungere GC a un vecchio linguaggio non GC-edito, è in realtà più facile creare una nuova lingua che abbia la maggior parte della stessa sintassi mentre si supporta un GC. Java e C # sono buoni esempi di questo.

    
risposta data 04.10.2014 - 21:51
fonte
5

Ci sono vari problemi, tra cui ...

  • Sebbene GC sia stato inventato prima del C ++, e possibilmente prima di C, sia C che C ++ sono stati implementati prima che i GC fossero ampiamente accettati come pratici.
  • Non è possibile implementare facilmente una lingua e una piattaforma GC senza un linguaggio non GC sottostante.
  • Anche se GC è dimostrabilmente più efficiente rispetto a non GC per codice di applicazioni tipiche sviluppato in scale temporali tipiche, ci sono problemi in cui un maggiore sforzo di sviluppo è un buon compromesso e la gestione specializzata della memoria supererà un GC generico. Inoltre, C ++ è in genere dimostrabilmente più efficiente della maggior parte dei linguaggi GC, anche senza ulteriori sforzi di sviluppo.
  • GC non è universalmente più sicuro di RAI in stile C ++. RAII consente di ripulire automaticamente risorse diverse dalla memoria, fondamentalmente perché supporta distruttori affidabili e tempestivi. Questi non possono essere combinati con i metodi GC convenzionali a causa di problemi con i cicli di riferimento.
  • I linguaggi GC hanno i loro tipi caratteristici di perdite di memoria, in particolare relative alla memoria che non sarà mai più utilizzata, ma dove esistevano riferimenti esistenti che non sono mai stati cancellati o sovrascritti. La necessità di farlo in modo esplicito non è diversa in linea di principio rispetto alla necessità di delete o free esplicitamente. L'approccio GC ha ancora un vantaggio - nessun riferimento ciondolante - e l'analisi statica può catturare alcuni casi, ma ancora una volta, non esiste una soluzione perfetta per tutti i casi.

Fondamentalmente, in parte si tratta dell'età delle lingue, ma ci sarà sempre un posto per linguaggi non GC - anche se è un po 'un posto di nicchia. E seriamente, in C ++, la mancanza di GC non è un grosso problema - la tua memoria è gestita in modo diverso, ma non è non gestita.

Il C ++ gestito da microsof ha almeno una certa capacità di mescolare GC e non GC nella stessa applicazione, permettendo un mix-and-match dei vantaggi di ciascuno, ma non ho l'esperienza per dire quanto bene funzioni in pratica.

Collegamenti di prostituzione alle relative risposte ...

risposta data 23.05.2017 - 13:33
fonte
4

La raccolta di dati inutili è fondamentalmente incompatibile con un linguaggio di sistema utilizzato per lo sviluppo di driver per hardware con funzionalità DMA.

È del tutto possibile che l'unico puntatore a un oggetto venga memorizzato in un registro hardware in alcune periferiche. Dal momento che il garbage collector non lo saprebbe, penserebbe che l'oggetto fosse irraggiungibile e lo raccolga.

Questo argomento vale doppio per la compattazione di GC. Anche se si è stati attenti a mantenere i riferimenti in memoria agli oggetti utilizzati dalle periferiche hardware, quando il GC ha spostato l'oggetto, non saprebbe come aggiornare il puntatore contenuto nel registro di configurazione delle periferiche.

Quindi ora avresti bisogno di una miscela di buffer DMA immobili e oggetti gestiti da GC, il che significa che hai tutti gli svantaggi di entrambi.

    
risposta data 15.10.2011 - 05:59
fonte
3

Perché, C & I C ++ sono linguaggi di livello relativamente basso pensati per scopi generali, anche, per esempio, per funzionare su un processore a 16 bit con 1 MB di memoria in un sistema embedded, che non può permettersi spreco di memoria con gc.

    
risposta data 09.10.2011 - 06:38
fonte
2

Ci sono garbage collector in C ++ e C. Non sai come funziona in C, ma in C ++ puoi usare RTTI per scoprire dinamicamente il tuo oggetto grafico e utilizzarlo per la garbage collection.

Per quanto ne so, non puoi scrivere Java senza un garbage collector. Una piccola ricerca ha trovato questo .

La differenza chiave tra Java e C / C ++ è che in C / C ++ la scelta è sempre tua, mentre in Java spesso non hai opzioni di design.

    
risposta data 23.05.2017 - 14:40
fonte
2

È un compromesso tra prestazioni e sicurezza.

Non è garantito che i tuoi rifiuti vengano raccolti in Java, quindi potrebbe rimanere inutilizzato a utilizzare lo spazio per un lungo periodo, mentre la scansione di oggetti senza riferimento (ad esempio la spazzatura) richiede più tempo dell'eliminazione o della liberazione di un inutilizzato oggetto.

Il vantaggio è, naturalmente, che si può costruire una lingua senza puntatori o senza perdite di memoria, quindi è più probabile che si produca codice corretto.

A volte può esserci un leggero margine "religioso" in questi dibattiti: sii avvertito!

    
risposta data 09.10.2011 - 21:36
fonte
1

Ecco un elenco di problemi inerenti a GC, che lo rendono inutilizzabile in un linguaggio di sistema come C:

  • Il GC deve essere eseguito al di sotto del livello del codice di cui gestisce gli oggetti. Semplicemente non c'è un tale livello in un kernel.

  • Un GC deve interrompere il codice gestito di volta in volta. Ora pensa a cosa succederebbe se facesse ciò nel tuo kernel. Tutte le elaborazioni sulla tua macchina si fermerebbero per, diciamo, un millisecondo, mentre il GC analizza tutte le allocazioni di memoria esistenti. Questo ucciderebbe tutti i tentativi di creare sistemi che operano secondo rigidi requisiti in tempo reale.

  • Un GC deve essere in grado di distinguere tra puntatori e non puntatori. Cioè, deve essere in grado di esaminare ogni oggetto di memoria esistente ed essere in grado di produrre un elenco di offset in cui è possibile trovare i suoi puntatori.

    Questa scoperta deve essere perfetta: il GC deve essere in grado di inseguire tutti i puntatori che scopre. Se avesse dereferenziato un falso positivo, probabilmente si sarebbe schiantato. Se non è riuscito a scoprire un falso negativo, probabilmente distruggerebbe un oggetto che è ancora in uso, causando il crash del codice gestito o danneggiando silenziosamente i suoi dati.

    Questo richiede assolutamente che le informazioni sul tipo siano memorizzate in ogni singolo oggetto esistente. Tuttavia, sia C che C ++ consentono semplici oggetti di dati vecchi che non contengono informazioni di tipo.

  • GC è un business intrinsecamente lento. I programmatori che sono stati socializzati con Java potrebbero non rendersene conto, ma i programmi possono essere ordini di grandezza più veloci quando non sono implementati in Java. E uno dei fattori che rendono Java lento è GC. Questo è ciò che impedisce ai linguaggi GCed come Java di essere utilizzati nel supercomputing. Se la tua macchina costa un milione di anni in consumi energetici, non vuoi pagare nemmeno il 10% di quella per la garbage collection.

C e C ++ sono lingue create per supportare tutti i possibili casi d'uso. E, come vedi, molti di questi casi d'uso sono preclusi dalla garbage collection. Quindi, al fine di supportare questi casi d'uso, C / C ++ non può essere garbage collection.

    
risposta data 06.09.2015 - 09:23
fonte

Leggi altre domande sui tag