Perché linguaggi come C e C ++ non hanno Garbage Collection, mentre Java lo fa? [chiuso]


57

Bene, so che ci sono cose come malloc / free per C e new / using-a-destructor per la gestione della memoria in C ++, ma mi chiedevo perché non ci sono "nuovi aggiornamenti" in queste lingue che consentono all'utente di hai la possibilità di gestire manualmente la memoria o che il sistema la esegua automaticamente (garbage collection)?

Un po 'una domanda nuova, ma è stato in CS solo per circa un anno.


9
Questo semestre abbiamo un modulo per lo sviluppo di iPhone. Dopo aver codificato le app per Android per 2 anni, questa domanda ha colpito la maggior parte della classe piuttosto difficile. Solo ora vediamo quante ore Java ci ha effettivamente salvato nel non dover rintracciare cattivi errori di gestione della memoria e non dover scrivere il codice della piastra della caldaia.
Siamii,

7
@NullUserException, poiché non specifica un modo per recuperare la memoria che implica praticamente un GC.
Winston Ewert,

1
@ bizso09: Hai già guardato ARC? Non è necessario un GC lento / grasso / non deterministico quando si
dispone del

3
Le risposte a questa bella domanda sono piene di stronzate religiose.
abatishchev

1
In C e C ++ è possibile prendere un puntatore, inserirlo in int e aggiungere un numero ad esso. Successivamente sottrae il numero dall'int e riporta il risultato a un puntatore. Otterrai lo stesso puntatore di prima. Buona fortuna per l'implementazione di un GC che NON raccoglie quella memoria mentre il suo indirizzo è memorizzato solo nella variabile che ha anche un altro valore. So che l'esempio è sciocco ma un elenco collegato XOR usa qualcosa di simile. Pubblicherei questo come risposta ma la domanda è chiusa.
Marian Spanik,

Risposte:


71

La garbage collection richiede strutture di dati per tracciare le allocazioni e / o il conteggio dei riferimenti. Questi creano sovraccarico di memoria, prestazioni e complessità del linguaggio. C ++ è progettato per essere "vicino al metallo", in altre parole, prende il lato di prestazioni più elevate delle funzionalità di compromesso rispetto alla 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 si trovano in librerie, sia commerciali che open source, piuttosto che parte del linguaggio stesso. Il conteggio dei riferimenti per gestire la durata degli oggetti non è lo stesso della garbage collection, ma affronta molti degli stessi problemi e si adatta meglio all'approccio di base di C ++.


26
Un problema secondario è che il GC non è deterministico. L'oggetto potrebbe essere o non essere ancora in memoria per molto tempo dopo che il programma lo ha "rilasciato". I cicli di vita di refcount sono deterministici, quando l'ultimo riferimento viene eliminato, la memoria viene eliminata. Ciò ha implicazioni non solo per l'efficienza della memoria, ma anche per il debug. Un errore di programmazione comune è l'oggetto "zombie", memoria di riferimento che è stata teoricamente eliminata. GC ha molte più probabilità di mascherare questo effetto e di produrre bug intermittenti ed estremamente difficili da rintracciare.
kylben,

22
- i gc moderni non tengono traccia delle allocazioni o contano i riferimenti. Costruiscono un grafico da tutto ciò che è attualmente in pila e semplicemente condensano e cancellano tutto il resto (semplificato), e GC normalmente si traduce in una riduzione della complessità del linguaggio. Anche il vantaggio in termini di prestazioni è discutibile.
Joel Coehoorn,

13
Ehm, @kylben, il punto centrale di avere GC automatico inserito nella lingua è che diventa impossibile fare riferimento a oggetti zombi, perché il GC libera solo oggetti che sono impossibili da fare riferimento! Ottieni il tipo di bug difficili da rintracciare di cui stai parlando quando commetti errori con la gestione manuale della memoria.
Ben

14
-1, GC non conta i riferimenti. Inoltre, a seconda dell'utilizzo della memoria e dello schema di allocazione, un GC può essere più veloce (con un sovraccarico nell'uso della memoria). Quindi anche l'argomento relativo alle prestazioni è fallace. Solo il vicino al metallo è in realtà un punto valido.
deadalnix,

14
Né Java né C # usano il conteggio dei riferimenti: gli schemi GC basati sul conteggio dei riferimenti sono piuttosto primitivi in ​​confronto e funzionano molto peggio dei moderni garbage collector (principalmente perché hanno bisogno di fare scritture di memoria per modificare il conteggio dei riferimenti ogni volta che copi un riferimento!)
mikera

44

A rigor di termini, non esiste alcuna gestione della memoria nel linguaggio C. malloc () e free () non sono parole chiave nella lingua, ma solo funzioni che vengono 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 conforme standard di C, ma questo non era sempre vero in passato.

Perché vorresti una lingua 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 beneficiare o addirittura 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 propria. Questo semplicemente non è accettabile in alcune situazioni ad 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 codice corretto e privo di bug.


2
+1 uno per la buona risposta generale, ma soprattutto per "Il prezzo pagato è che il linguaggio C fornisce pochissimo aiuto nella scrittura di un codice corretto e privo di bug"
Shivan Dragon

2
C ha una gestione della memoria, ma funziona, quindi la gente a malapena lo nota. C'è memoria statica, registri e stack. Fino a quando non inizi a stanziare dal mucchio, stai bene e dandy. È l'allocazione dell'heap che rovina le cose. Per quanto riguarda Java, tutti possono scrivere il proprio runtime Java: c'è molto da scegliere, incluso quello che potrebbe essere chiamato "Java di sistema". .NET può fare praticamente tutto ciò che può fare C - è solo in ritardo rispetto alle capacità native di C ++ (ad es. Le classi sono gestite solo in .NET). Naturalmente, hai anche C ++. NET, che ha tutto ciò che fa C ++ e tutto ciò che fa .NET.
Luaan,

1
@Luaan Direi che è una definizione molto generosa di "gestione della memoria" "Fino a quando non inizi a stanziare dal mucchio, stai bene e dandy. È l'allocazione del mucchio che rovina le cose", È come dire che un'auto è un aereo perfettamente buono, semplicemente non è in grado di volare.
Charles E. Grant,

1
@ CharlesE.Grant Bene, un linguaggio puramente funzionale può fare tutto con quel tipo di gestione della memoria. Solo perché l'allocazione dell'heap è un buon compromesso in alcuni casi d'uso non significa che sia il punto di riferimento per tutte le lingue / runtime. Non è che la gestione della memoria smetta di essere "gestione della memoria" solo perché è semplice, diretta, nascosta dietro le quinte. Progettare l'allocazione di memoria statica è ancora una gestione della memoria, così come un buon uso dello stack e di qualsiasi altra cosa tu abbia a disposizione.
Luaan,

"qualsiasi implementazione conforme allo standard" non è vera, solo per l'implementazione dell'ambiente host conforme allo standard. Alcune piattaforme / librerie standard, la maggior parte per microcontrollore incorporato a 8 o 16 bit, non forniscono malloc()o free(). (ad esempio i compilatori MLAP per PIC)
12431234123412341234123

32

La vera risposta è che l'unico modo per creare un meccanismo di raccolta dei rifiuti sicuro ed efficiente è disporre di un supporto a livello di lingua per riferimenti opachi. (O, al contrario, una mancanza di supporto a livello di lingua 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 oggetti allocati in memoria , che è cruciale per un'implementazione GC ad alte prestazioni.

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

Questo approccio presenta vantaggi e svantaggi: il lato positivo è che le allocazioni di heap in una lingua che supporta GC sono veloci quanto le allocazioni di stack in una lingua che non supporta GC, e il lato negativo è che gli oggetti che devono eseguire la pulizia prima di essere distrutti richiedono un meccanismo separato (ad esempio la usingparola chiave di C # ) oppure il loro codice di pulizia viene eseguito in modo non deterministico.

Si noti che una chiave per un GC ad alte prestazioni è che ci deve essere 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 da parte dell'operatore, potrebbe emulare un tipo di puntatore GC'd, sebbene debba essere eseguito con attenzione. Infatti, quando Microsoft ha inventato il suo dialetto di C ++ che sarebbe stato eseguito sotto il CLR (il runtime .NET), hanno dovuto inventare una nuova sintassi per "riferimenti in stile C #" (ad esempio Foo^) per distinguerli da "riferimenti in stile C ++" (ad es Foo&.).

Ciò che ha C ++ e ciò che viene regolarmente utilizzato dai programmatori C ++ sono i 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 davvero a una funzionalità di progettazione del linguaggio. C ha fatto una scelta, 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 d'argento, ma avere familiarità con le diverse scelte là fuori ti aiuterà a scegliere quella corretta per qualsiasi programma che stai attualmente cercando di costruire.


4
Questa è la vera risposta alla domanda
coredump,

1
Per la parte c ++, al giorno d'oggi dovresti vedere std :: unique_ptr e std :: move :)
Niclas Larsson

@NiclasLarsson: non sono sicuro di aver capito il tuo punto. Stai dicendo che std::unique_ptrè "supporto a livello linguistico per riferimenti opachi"? (Non era il tipo di supporto che intendevo dire, e inoltre non penso che sia sufficiente a meno che il supporto per la manipolazione diretta della memoria non sia stato rimosso dal C ++.) Nella mia risposta cito puntatori intelligenti e considererei std:unique_ptrun puntatore intelligente , poiché in realtà esegue il conteggio dei riferimenti, supporta solo i casi speciali in cui il numero di riferimenti è zero o uno (ed std::moveè il meccanismo di aggiornamento del conteggio dei riferimenti).
Daniel Pryden,

std::unique_ptrnon ha un conteggio dei riferimenti e std::movenon ha nulla a che fare con i riferimenti (quindi "nessun" hit delle prestazioni). Vedo il tuo punto però, così come std::shared_ptrun conteggio dei riferimenti che è implicitamente aggiornato da std::move:)
Niclas Larsson

2
@ Mike76: dal lato dell'allocazione, un allocatore GC funzionerà alla stessa velocità dell'allocazione dello stack e il GC può deallocare migliaia di oggetti contemporaneamente. Indipendentemente da ciò che fai con un'implementazione di conteggio dei riferimenti, l'allocazione e la deallocazione non saranno mai più veloci di malloce free. Quindi sì, un GC può essere sostanzialmente più veloce. (Nota che ho detto "può essere" - ovviamente l'esatta esecuzione di ciascun programma è influenzata da molti fattori.)
Daniel Pryden,

27

Perché, quando si utilizza la potenza del C ++, non è necessario.

Herb Sutter: " Non scrivo cancellare da anni. "

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

Potrebbe sorprendere molti programmatori C ++ esperti.


Interessante. Il mio materiale di lettura per oggi.
surfasb,

Bah, un video. Ma mai meno, interessante già.
surfasb,

2
video interessante. 21 minuti e 55 minuti sono stati i pezzi migliori. Peccato che le chiamate WinRT sembravano ancora bumpf C ++ / CLI.
gbjbaanb,

2
@ dan04: è vero. Ma poi, se scrivi in ​​C, ottieni quello che chiedi.
DeadMG

6
Gestire i puntatori intelligenti non è più impegnativo che assicurarsi di non avere riferimenti non necessari in un ambiente di raccolta rifiuti. Perché GC non riesce a leggere la tua mente, non è neanche magia.
Tamás Szelei,

15

"Tutto" è un garbage collector è un processo che viene eseguito periodicamente controllando se sono presenti oggetti non referenziati in memoria e se sono eliminati. (Sì, so che questa è una semplificazione eccessiva). Questa non è una proprietà della lingua, ma il framework.

Ci sono netturbini scritti per C e C ++ - questo uno per esempio.

Uno dei motivi per cui uno non è stato "aggiunto" alla lingua potrebbe essere a causa del volume di codice esistente che non lo userebbe mai poiché usano il proprio codice per gestire la memoria. Un altro motivo potrebbe essere che i tipi di applicazioni scritte in C e C ++ non necessitano dell'overhead associato a un processo di garbage collection.


1
Ma i futuri programmi scritti inizierebbero a usare il Garbage Collector, no?
Templare oscuro,

5
Mentre la garbage collection è teoricamente indipendente da qualsiasi linguaggio di programmazione, è abbastanza difficile scrivere un GC utile per C / C ++ e persino impossibile crearne uno a prova di errore (almeno tanto a prova di errore come Java) - il motivo per cui Java può estrarlo spento è perché funziona in un ambiente virtualizzato controllato. Al contrario, il linguaggio Java è progettato per GC e avrai difficoltà a scrivere un compilatore Java che non fa GC.
martedì

4
@tdammers: sono d'accordo che la raccolta dei rifiuti debba essere supportata dalla lingua per essere possibile. Tuttavia, il punto principale non è la virtualizzazione e l'ambiente controllato, ma la tipizzazione rigorosa. C e C ++ sono tipizzati in modo debole, quindi consentono cose come la memorizzazione del puntatore nella variabile intera, la ricostruzione dei puntatori dagli offset e tali cose che impediscono al raccoglitore di essere in grado di dire in modo affidabile ciò che è raggiungibile (C ++ 11 vieta almeno collezionisti conservatori). In Java sai sempre cosa è un riferimento, quindi puoi raccoglierlo con precisione, anche se compilato come nativo.
Jan Hudec,

2
@ ThorbjørnRavnAndersen: posso scrivere un programma C valido che memorizza i puntatori in modo tale che nessun garbage collector possa mai trovarli. Se quindi si aggancia un garbage collector a malloce free, si romperà il mio programma corretto.
Ben Voigt,

2
@ ThorbjørnRavnAndersen: No, non chiamerei freefino a quando non avrò finito. Ma il tuo garbage collector proposto che non libera la memoria finché non chiamo esplicitamente freenon è affatto un garbage collector.
Ben Voigt,

12

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


9

Non ho le virgolette esatte ma sia Bjarne che Herb Sutter dicono qualcosa del genere:

C ++ non ha bisogno di un garbage collector, perché non ha spazzatura.

Nel moderno C ++ usi i puntatori intelligenti e quindi non hai immondizia.


1
quali sono i puntatori intelligenti?
Templare oscuro,

11
se fosse stato così semplice, nessuno avrebbe implementato alcun GC.
deadalnix,

7
@deadalnix: Giusto, perché nessuno implementa mai qualcosa di eccessivamente complicato, lento, gonfio o superfluo. Tutto il software è sempre efficiente al 100%, giusto?
Zach,

5
@deadalnix - L'approccio C ++ alla gestione della memoria è più recente di Garbage Collector. RAII è stato inventato da Bjarne Stroustrup per C ++. La pulizia dei distruttori è un'idea più vecchia, ma le regole per garantire la sicurezza delle eccezioni sono fondamentali. Non so quando esattamente quando l'idea stessa fu descritta per la prima volta, ma il primo standard C ++ fu finalizzato nel 1998, e Stroustrups "Design and Evolution of C ++" non fu pubblicato fino al 1994, e le eccezioni furono un'aggiunta relativamente recente al C ++ - dopo la pubblicazione del "Manuale di riferimento C ++ annotato" nel 1990, credo. GC è stato inventato nel 1959 per Lisp.
Steve314,

1
@deadalnix - sei a conoscenza del fatto che almeno una VM Java ha utilizzato un GC conteggio dei riferimenti che potrebbe (quasi) essere implementato utilizzando RAII in stile C ++ utilizzando una classe di puntatore intelligente - proprio perché era più efficiente per il codice multithread rispetto alle VM esistenti? Vedi www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf. Uno dei motivi per cui non si vede questo in C ++ nella pratica è la solita raccolta GC: può raccogliere cicli, ma non può scegliere un ordine distruttore sicuro in presenza di cicli e quindi non può garantire una pulizia affidabile dei distruttori.
Steve314,

8

Ti chiedi perché queste lingue non sono state aggiornate per includere un garbage collector opzionale.

Il problema con la garbage collection opzionale è che non è possibile mescolare il codice che utilizza i diversi modelli. Cioè, se scrivo un codice che presume che tu stia utilizzando un garbage collector, non puoi usarlo nel tuo programma che ha la garbage collection disattivata. Se lo fai, perderà dappertutto.


6

Riesci a immaginare di scrivere un gestore di dispositivi in ​​una lingua con Garbage Collection? Quanti bit potrebbero scendere lungo la linea mentre il GC era in esecuzione?

O un sistema operativo? Come hai potuto avviare l'esecuzione della garbage collection prima ancora di avviare il kernel?

C è progettato per un livello basso vicino alle attività hardware. Il problema? è un linguaggio così carino che è una buona scelta anche per molte attività di livello superiore. Gli zar del linguaggio sono consapevoli di questi usi ma devono supportare i requisiti dei driver di dispositivo, del codice incorporato e dei sistemi operativi come priorità.


2
C buono per alto livello? Sbuffai da bere su tutta la tastiera.
DeadMG

5
Bene, ha detto "molti compiti di livello superiore". Egli potrebbe essere troll-conteggio (uno, due, molti ...). E in realtà non ha detto più in alto di cosa. Scherzi a parte, però, è vero - l'evidenza è che molti importanti progetti di livello superiore sono stati sviluppati con successo in C. Ora ci possono essere scelte migliori per molti di questi progetti, ma un progetto di lavoro è una prova più forte della speculazione su ciò che potrebbe sono stato.
Steve314,

Esistono alcuni sistemi operativi gestiti e funzionano piuttosto bene. In effetti, quando gestisci l'intero sistema, il rendimento ottenuto dall'uso del codice gestito diminuisce ancora di più, fino a diventare più veloce del codice non gestito in scenari di vita reale. Naturalmente, questi sono tutti "sistemi operativi di ricerca": non c'è praticamente modo di renderli compatibili con il codice non gestito esistente oltre a creare un sistema operativo non gestito completamente virtualizzato all'interno del sistema operativo gestito. Microsoft ha suggerito ad un certo punto che potrebbero sostituire Windows Server con uno di questi, tuttavia, poiché sempre più codice server è scritto su .NET.
Luaan,

6

La risposta breve e noiosa a questa domanda è che deve esserci un linguaggio raccolto non spazzatura là fuori per le persone che scrivono i garbage collector. Concettualmente non è facile avere un linguaggio che allo stesso tempo consenta un controllo molto preciso sul layout della memoria e che abbia un GC in esecuzione.

L'altra domanda è perché C e C ++ non hanno garbage collector. Bene, 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 C ++ in questa età non è proprio il tipo che manca a un GC.

Inoltre, invece di aggiungere GC a un vecchio linguaggio non GC-ed, in realtà è più facile creare un nuovo linguaggio che abbia la stessa sintassi supportando un GC. Java e C # ne sono un buon esempio.


1
Da qualche parte su programmers.se o SO, qualcuno sostiene che qualcuno stesse lavorando a un dispositivo auto-bootstrapping che raccoglieva immondizia: IIRC fondamentalmente implementava la VM usando un linguaggio GC, con un sottoinsieme bootstrapping usato per implementare il GC stesso. Ho dimenticato il nome Quando l'ho esaminato, ho scoperto che praticamente non avevano mai fatto il salto dal sottoinsieme senza GC al livello di GC funzionante. Questo è possibile in linea di principio, ma AFAIK non è mai stato realizzato in pratica - è certamente un caso di fare le cose nel modo più duro.
Steve314,

@ Steve314: mi piacerebbe vederlo se mai ti ricordi dove l'hai trovato!
hugomg,

trovato! Vedere i commenti su stackoverflow.com/questions/3317329/… riferendosi alla Klein VM. Parte del problema trovandolo - la domanda è stata chiusa.
Steve314,

A proposito, non riesco a iniziare i miei commenti con @missingno - cosa dà?
Steve314,

@ steve314: dopo aver scritto la risposta a questa discussione, ricevo già una notifica per tutti i commenti. Fare un @ -post in questo caso sarebbe ridondante e non è permesso da SE (non chiedermi perché ). (La vera causa però è perché manca il mio numero)
hugomg

5

Ci sono vari problemi, tra cui ...

  • Sebbene GC sia stato inventato prima del C ++, e probabilmente prima del C, sia il C che il C ++ furono implementati prima che i GC fossero ampiamente accettati come pratici.
  • Non è possibile implementare facilmente un linguaggio e una piattaforma GC senza un linguaggio non GC sottostante.
  • Sebbene GC sia dimostrabilmente più efficiente di un non GC per il tipico codice di applicazioni sviluppato in tempi tipici, ecc., Ci sono problemi in cui un maggiore sforzo di sviluppo è un buon compromesso e la gestione della memoria specializzata supererà un GC generico. Inoltre, il C ++ è tipicamente più efficiente della maggior parte dei linguaggi GC anche senza ulteriori sforzi di sviluppo.
  • GC non è universalmente più sicuro di RAII 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 per quanto riguarda la memoria che non verrà mai più utilizzata, ma dove esistevano riferimenti esistenti che non sono mai stati annullati o sovrascritti. La necessità di farlo esplicitamente non è diversa in linea di principio rispetto alla necessità deleteo freeesplicitamente. L'approccio GC ha ancora un vantaggio - nessun riferimento penzolante - e l'analisi statica può rilevare alcuni casi, ma ancora una volta non esiste una soluzione perfetta per tutti i casi.

Fondamentalmente, in parte riguarda l'età delle lingue, ma ci sarà comunque sempre posto per le lingue 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 Microsoft ha almeno una certa capacità di mescolare GC e non GC nella stessa applicazione, consentendo un mix-and-match dei vantaggi di ciascuno, ma non ho l'esperienza per dire quanto funzioni in pratica.

Collegamenti di puttana alle mie risposte correlate ...


4

La garbage collection è fondamentalmente incompatibile con un linguaggio di sistema utilizzato per lo sviluppo di driver per hardware compatibile con DMA.

È del tutto possibile che l'unico puntatore a un oggetto venga archiviato in un registro hardware in alcune periferiche. Dato che il garbage collector non lo sapeva, avrebbe pensato che l'oggetto non fosse raggiungibile e lo avrebbe raccolto.

Questo argomento vale doppio per la compattazione di GC. Anche se stai attento a mantenere i riferimenti in memoria agli oggetti utilizzati dalle periferiche hardware, quando il GC riposiziona 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.


Probabilmente tutti gli svantaggi di entrambi, ma meno casi di ciascuno svantaggio e lo stesso per i vantaggi. Chiaramente c'è complessità nell'avere più tipi di gestione della memoria da affrontare, ma può anche essere evitata la complessità scegliendo il cavallo giusto per ogni corso all'interno del proprio codice. Improbabile, immagino, ma c'è un vuoto teorico lì. Ho già ipotizzato di mescolare GC e non GC nella stessa lingua in precedenza, ma non per i driver di dispositivo, più per avere un'applicazione principalmente GC, ma con alcune librerie di strutture dati di basso livello gestite manualmente dalla memoria.
Steve314,

@ Steve314: Non diresti che ricordare quali oggetti devono essere liberati manualmente è oneroso come ricordare di liberare tutto? (Naturalmente, i puntatori intelligenti possono aiutare con entrambi, quindi nessuno dei due è un grosso problema) E hai bisogno di pool diversi per oggetti gestiti manualmente rispetto a oggetti raccolti / compattabili, poiché la compattazione non funziona bene quando ci sono oggetti fissi sparsi ovunque. Quindi molta complessità extra per niente.
Ben Voigt,

2
Non se c'è una chiara divisione tra il codice di alto livello che è tutto GC e il codice di basso livello che esce da GC. Ho sviluppato l'idea principalmente mentre guardavo D alcuni anni fa, il che ti consente di rinunciare a GC ma non ti consente di riaccedere. Prendi ad esempio una libreria B + tree. Il contenitore nel suo insieme dovrebbe essere GC, ma probabilmente i nodi della struttura dei dati no: è più efficiente eseguire una scansione personalizzata attraverso i nodi foglia piuttosto che fare in modo che il GC esegua una ricerca ricorsiva attraverso i nodi della diramazione. Tuttavia, tale scansione deve riportare gli elementi contenuti al GC.
Steve314,

Il punto è che questa è una funzionalità contenuta. Trattare i nodi B + albero come la gestione della memoria speciale WRT non è diverso a trattarli come speciale WRT essere nodi B + tree. È una libreria incapsulata e il codice dell'applicazione non ha bisogno di sapere che il comportamento GC è stato ignorato / inserito in casi speciali. Tranne il fatto che, almeno in quel momento, era impossibile in D - come ho detto, non c'era modo di riaccedere e riferire gli elementi contenuti al GC come potenziali radici GC.
Steve314,

3

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


1
"Sistema incorporato"? All'epoca in cui C era standardizzato (1989), doveva essere in grado di gestire PC con 1 MB di memoria.
dan04,

Sono d'accordo, stavo citando un esempio più attuale.
Petruza,

1MB ??? Santo schmoley, chi avrebbe mai avuto bisogno di tanta RAM? </billGates>
Mark K Cowan,

2

Esistono garbage collector in C ++ e C. Non sei sicuro di come funzioni in C, ma in C ++ puoi sfruttare RTTI per scoprire dinamicamente il tuo grafico a oggetti e usarlo per Garbage Collection.

Per quanto ne so, non è possibile scrivere Java senza un Garbage Collector. Una piccola ricerca ha rivelato 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 progettazione.


E anche che i garbage collector dedicati siano meglio implementati, più efficienti e si adattino meglio al linguaggio. :)
Max

No, non puoi usare RTTI per scoprire dinamicamente il grafico degli oggetti in C / C ++: sono i semplici vecchi oggetti dati che rovinano tutto. Semplicemente non ci sono informazioni RTTI memorizzate in un semplice oggetto dati vecchio che consentirebbe a un garbage collector di distinguere tra puntatori e non puntatori all'interno di quell'oggetto. Ancora peggio, i puntatori non devono essere perfettamente allineati su tutto l'hardware, quindi, dato un oggetto a 16 byte, ci sono 9 possibili posizioni in cui è possibile memorizzare un puntatore a 64 bit, solo due dei quali non si sovrappongono.
cmaster

2

È un compromesso tra prestazioni e sicurezza.

Non vi è alcuna garanzia che i rifiuti vengano raccolti in Java, quindi potrebbe rimanere inutilizzato per molto tempo, mentre la scansione di oggetti non referenziati (ad esempio i rifiuti) richiede anche più tempo rispetto all'eliminazione o alla liberazione esplicita di un oggetto non utilizzato.

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

A volte questi dibattiti possono avere un leggero vantaggio "religioso": attenzione!


1

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

  • Il GC deve funzionare al di sotto del livello del codice di cui gestisce gli oggetti. Semplicemente non esiste un tale livello in un kernel.

  • Un GC deve arrestare il codice gestito di volta in volta. Ora pensa a cosa succederebbe se lo facesse al tuo kernel. Tutta l'elaborazione sulla macchina si arresterebbe, per esempio, per un millisecondo, mentre il GC esegue la scansione di tutte le allocazioni di memoria esistenti. Ciò ucciderebbe tutti i tentativi di creare sistemi che funzionino con severi requisiti in tempo reale.

  • Un GC deve essere in grado di distinguere tra puntatori e non puntatori. Cioè, deve essere in grado di guardare 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 dereferenziasse un falso positivo, probabilmente si schianterebbe. Se non fosse in grado di rilevare un falso negativo, probabilmente distruggerebbe un oggetto ancora in uso, causando l'arresto anomalo del codice gestito o corrompendo silenziosamente i suoi dati.

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

  • GC è un'attività intrinsecamente lenta. 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 lento Java è GC. Questo è ciò che impedisce ai linguaggi GCed come Java di essere utilizzati nel supercalcolo. Se la tua macchina costa un milione di consumi energetici all'anno, non vuoi pagare nemmeno il 10% di quello per la raccolta dei rifiuti.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.