Svantaggi della gestione della memoria basata su ambito


38

Mi piace molto la gestione della memoria basata sull'ambito (SBMM) o RAII , in quanto viene più comunemente (confusamente?) Indicato dalla comunità C ++. Per quanto ne so, ad eccezione di C ++ (e C), non esiste nessun altro linguaggio corrente oggi in uso che rende SBMM / RAII il loro principale meccanismo di gestione della memoria, e preferiscono invece usare Garbage Collection (GC).

Lo trovo piuttosto confuso, da allora

  1. SBMM rende i programmi più deterministici (puoi dire esattamente quando un oggetto viene distrutto);
  2. nei linguaggi che usano GC devi spesso fare una gestione manuale delle risorse (vedi la chiusura dei file in Java, per esempio), che in parte vanifica lo scopo di GC ed è anche soggetto a errori;
  3. la memoria heap può anche (molto elegantemente, imo) essere legata all'ambito (vedi std::shared_ptrin C ++).

Perché SBMM non è più ampiamente utilizzato? Quali sono i suoi svantaggi?


1
Alcuni svantaggi (soprattutto per quanto riguarda la velocità) sono discussi in Wikipedia: en.wikipedia.org/wiki/…
Philipp

2
Il problema di gestione manuale delle risorse di Java è un effetto collaterale di non garantire che il finalize()metodo di un oggetto verrà chiamato prima della garbage collection. In effetti, ciò crea la stessa classe di problemi che si suppone risolva la garbage collection.
Blrfl

7
@Blrfl Nonsense. La gestione delle risorse "manuale" (per risorse diverse dalla memoria, ovviamente) sarebbe preferibile anche se quel "problema" non esistesse, perché GC può funzionare molto tempo dopo che la risorsa non viene utilizzata o addirittura non funzionare affatto. Non è un problema per la memoria e la gestione della memoria è tutto ciò che la raccolta dei rifiuti dovrebbe risolvere.

4
btw. Mi piace fare riferimento ad esso come SBRM poiché è possibile utilizzare lo stesso meccanismo per gestire le risorse in generale, non solo la memoria.
PlasmaHH

Risposte:


27

Cominciamo postulando che la memoria è di gran lunga (decine, centinaia o addirittura migliaia di volte) più comune di tutte le altre risorse combinate. Ogni singola variabile, oggetto, membro oggetto necessita di memoria allocata e liberata in seguito. Per ogni file che apri, crei dozzine a milioni di oggetti per archiviare i dati estratti dal file. Ogni flusso TCP si accompagna a un numero illimitato di stringhe di byte temporanee create per essere scritte nel flusso. Siamo sulla stessa pagina qui? Grande.

Perché RAII funzioni (anche se hai puntatori intelligenti già pronti per ogni caso d'uso sotto il sole), devi avere la proprietà giusta. Devi analizzare chi dovrebbe possedere questo o quell'oggetto, chi non dovrebbe, e quando la proprietà dovrebbe essere trasferita da A a B. Certo, potresti usare la proprietà condivisa per tutto , ma poi emuleresti un GC tramite puntatori intelligenti. A quel punto diventa molto più facile e veloce costruire il GC nella lingua.

La garbage collection ti libera da questa preoccupazione per la risorsa di gran lunga più comunemente usata, la memoria. Certo, devi ancora prendere la stessa decisione per altre risorse, ma quelle sono molto meno comuni (vedi sopra) e anche la proprietà complicata (ad esempio condivisa) è meno comune. Il carico mentale è ridotto in modo significativo.

Ora, dai un nome ad alcuni aspetti negativi nel rendere tutti i valori raccolti. Tuttavia, l'integrazione di GC di sicurezza della memoria e tipi di valore con RAII in una lingua è estremamente difficile, quindi forse è meglio mimare questi compromessi con altri mezzi?

La perdita di determinismo si rivela in realtà non così grave in pratica, poiché influisce solo sulla durata deterministica dell'oggetto . Come descritto nel paragrafo successivo, la maggior parte delle risorse (a parte la memoria, che è abbondante e può essere riciclata piuttosto pigramente) non è destinata alla durata dell'oggetto in queste lingue. Ci sono alcuni altri casi d'uso, ma sono rari nella mia esperienza.

Il tuo secondo punto, la gestione manuale delle risorse, è oggi affrontato tramite un'istruzione che esegue la pulizia basata sull'ambito, ma non associa questa pulizia al tempo di vita dell'oggetto (quindi non interagisce con il GC e la sicurezza della memoria). Questo è usingin C #, within Python, try-con-risorse nelle recenti versioni di Java.


1
Questo non spiega perché i modelli non deterministici come GC dovrebbero essere superiori a quelli deterministici. usingle dichiarazioni sono possibili solo localmente. In questo modo è impossibile ripulire le risorse contenute nelle variabili membro.
Philipp

8
@Philipp La proprietà condivisa di tutto è un GC, solo molto scarso. Se prendi la "proprietà condivisa" per implicare il conteggio dei ref, ti preghiamo di dirlo, ma continua la discussione sui cicli nei commenti sulla risposta di Amon. Inoltre, non sono sicuro che il conteggio dei riferimenti sia deterministico nel senso in cui l'OP è interessato (gli oggetti vengono liberati il ​​più presto possibile, i cicli di attualizzazione, ma spesso non si capisce quando lo si sta guardando nel programma). Inoltre, il conteggio dei riferimenti per tutto è lento, molto più lento di un GC di tracciamento moderno.

16
usingè uno scherzo rispetto a RAII, solo per questo lo sai.
DeadMG

3
@Philipp, descrivi la tua metrica per "superior". È vero che la gestione manuale della memoria è più veloce in fase di esecuzione per gestire la gestione della memoria. Tuttavia, il costo del software non può essere valutato esclusivamente in base al tempo della CPU impiegato nella sola gestione della memoria.
Art

2
@ArTs: non sarei nemmeno necessariamente d'accordo con questo. RAII ha il requisito che un oggetto deve essere distrutto quando lascia l'ambito. È quindi necessario un ciclo per eseguire n distruzioni di oggetti. Sotto un moderno GC generazionale, quelle distruzioni potrebbero essere rinviate fino alla fine del ciclo, o anche più tardi, ed eseguire una sola operazione per distruggere centinaia di iterazioni degne di memoria. Un GC può essere molto molto veloce nei suoi buoni casi.
Phoshi

14

RAII segue anche la gestione automatica della memoria per il conteggio dei riferimenti, ad esempio utilizzata da Perl. Sebbene il conteggio dei riferimenti sia facile da implementare, deterministico e abbastanza performante, non può gestire riferimenti circolari (causano una perdita), motivo per cui non è comunemente usato.

Le lingue raccolte con immondizia non possono usare direttamente RAII, ma spesso offrono sintassi con un effetto equivalente. In Java, abbiamo l'istruzione try-with-ressource

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

che chiama automaticamente .close()la risorsa all'uscita del blocco. C # ha l' IDisposableinterfaccia, che consente .Dispose()di essere chiamato quando si lascia using (...) { ... }un'istruzione. Python ha la withdichiarazione:

with open(filename) as f:
    ...

che funziona in modo simile. In una svolta interessante, il metodo di apertura del file di Ruby ottiene un callback. Dopo l'esecuzione del callback, il file viene chiuso.

File.open(name, mode) do |f|
    ...
end

Penso che Node.js usi la stessa strategia.


4
L'uso di funzioni di ordine superiore per la gestione delle risorse risale a molto prima di Ruby. In Lisps, è abbastanza comune avere, diciamo, with-open-filehandlefunzioni che aprono il file, lo restituiscono a una funzione e al ritorno della funzione chiudono di nuovo il file.
Jörg W Mittag,

4
L'argomento ciclico di riferimento è abbastanza comune, ma quanto è veramente importante? I riferimenti ciclici possono essere mitigati usando puntatori deboli se la proprietà è chiara.
Philipp

2
@Philipp Quando usi il conteggio dei riferimenti, la proprietà di solito non è chiara. Inoltre, questa risposta parla di lingue che usano il conteggio dei riferimenti esclusivamente e automaticamente, quindi non esistono riferimenti deboli o sono molto più difficili da usare rispetto ai riferimenti forti.

3
Le strutture di dati ciclici di @Philipp sono molto rare, a meno che tu non stia comunque lavorando con grafici complicati. I puntatori deboli non aiutano in un grafico generale ad oggetti ciclici, anche se aiutano in casi più comuni come i puntatori padre in un albero. Una buona soluzione consiste nel mantenere un oggetto di contesto che rappresenta il riferimento all'intero grafico e gestisce la distruzione. Il refcounting non è un dealbraker, ma richiede che il programmatore sia molto consapevole delle sue restrizioni. Cioè ha un costo cognitivo leggermente più alto rispetto a GC.
amon

1
Un motivo importante per cui il conteggio dei riferimenti viene usato raramente è che è spesso più lento di GC, nonostante la sua semplicità.
Rufflewind

14

A mio avviso, il vantaggio più convincente della raccolta dei rifiuti è che consente la componibilità . La correttezza della gestione della memoria è una proprietà locale nell'ambiente di raccolta rifiuti. È possibile esaminare ciascuna parte separatamente e determinare se può perdere memoria. Combina un numero qualsiasi di parti corrette per la memoria e rimangono corrette.

Quando si fa affidamento sul conteggio dei riferimenti si perde quella proprietà. Se l'applicazione può perdere la memoria diventa una proprietà globale dell'intera applicazione con il conteggio dei riferimenti. Ogni nuova interazione tra le parti ha la possibilità di utilizzare la proprietà errata e interrompere la gestione della memoria.

Ha un effetto molto visibile sulla progettazione di programmi nelle diverse lingue. I programmi in linguaggi GC tendono ad essere un po 'più minuscoli di oggetti con molte interazioni, mentre in linguaggi senza GC si tende a preferire parti strutturate con interazioni strettamente controllate e limitate tra loro.


1
La correttezza è componibile solo se gli oggetti contengono solo riferimenti per proprio conto e non per conto dei target di tali riferimenti. Una volta che cose come le notifiche entrano nel mix (Bob tiene un riferimento a Joe perché Joe ha chiesto a Bob di avvisarlo quando è successo qualcosa e Bob ha promesso di farlo, ma Bob altrimenti non si preoccupa di Joe), la correttezza di GC spesso richiede una gestione delle risorse mirata [implementato manualmente in molti casi, poiché i sistemi GC mancano dell'automazione del C ++].
supercat

@supercat: "La correttezza GC spesso richiede la gestione delle risorse con ambito". Eh? L'ambito esiste solo nel codice sorgente e GC esiste solo in fase di esecuzione (ed è quindi completamente ignaro dell'esistenza dell'ambito).
Jon Harrop,

@JonHarrop: Stavo usando il termine "ambito" nello stesso senso di un "puntatore con ambito C ++" [la durata dell'oggetto dovrebbe essere quella del contenitore che lo contiene], poiché questo è l'uso implicito dalla domanda originale. Il mio punto è che gli oggetti creano riferimenti potenzialmente longevi a se stessi per scopi come la ricezione di eventi potrebbero non essere componibili in un sistema puramente GC. Per correttezza, alcuni riferimenti devono essere forti e alcuni riferimenti devono essere deboli, e quali riferimenti devono essere che dipenderanno da come viene utilizzato un oggetto. Ad esempio ...
supercat

... supponiamo che gli oggetti Fred e Barney si iscrivano per la notifica ogni volta che qualcosa in una determinata directory viene modificato. Il gestore di Fred non fa altro che incrementare un contatore il cui valore può riportare su richiesta, ma per il quale non ha altro uso. Il gestore di Barney aprirà una nuova finestra se un determinato file viene modificato. Per correttezza, Fred dovrebbe essere abbonato a un evento debole, ma quello di Barney dovrebbe essere forte, ma l'oggetto timer non avrà modo di saperlo.
supercat

@supercat: giusto. Non direi che succede "spesso". L'ho incontrato solo una volta in 30 anni di programmazione.
Jon Harrop,

7

Le chiusure sono una caratteristica essenziale di quasi tutte le lingue moderne. Sono molto facili da implementare con GC e molto difficili (anche se non impossibili) da ottenere con RAII, poiché una delle loro caratteristiche principali è che ti permettono di astrarre nel corso della vita delle tue variabili!

Il C ++ li ha acquisiti solo 40 anni dopo che lo hanno fatto tutti gli altri, e ci sono voluti un sacco di duro lavoro da parte di molte persone intelligenti per farli bene. Al contrario, molti linguaggi di scripting progettati e implementati da persone con zero conoscenze nella progettazione e implementazione dei linguaggi di programmazione li hanno.


9
Non credo che le chiusure in C ++ siano un buon esempio. Le lambda in C ++ 11 sono solo zucchero sintattico per le classi di funzioni (che precedono significativamente C ++ 11) e sono ugualmente insicure per la memoria: se catturi qualcosa per riferimento e chiami la chiusura dopo che quella cosa è morta, ottieni semplicemente UB, proprio come trattenere un riferimento più a lungo che valido. Il fatto che siano apparsi in ritardo di 40 anni è dovuto al riconoscimento tardivo di FP, non a capire come renderli sicuri. E mentre progettarli è stato certamente un compito enorme, dubito che la maggior parte degli sforzi siano stati presi in considerazione durante la vita.

Concordo con Delnan: il C ++ non ha ottenuto le chiusure corrette: è necessario programmarle con molta attenzione se non si desidera ottenere un dump principale quando le si invoca.
Giorgio

2
@delnan: lambda di cattura per riferimento ha intenzionalmente questa [&]sintassi. Qualsiasi programmatore C ++ associa già il &segno a riferimenti e conosce riferimenti non aggiornati.
Salterio

2
@MSalters Qual è il tuo punto? Ho disegnato io stesso la connessione di riferimento. Non ho detto che i lambda in C ++ sono eccezionalmente sicuri, ho detto che sono esattamente non sicuri come i riferimenti. Non ho sostenuto che i lambda in C ++ siano cattivi, ho discusso contro l'affermazione di questa risposta (che il C ++ ha ottenuto chiusure molto tardi perché hanno dovuto capire come farlo nel modo giusto).

5
  1. SBMM rende i programmi più deterministici (puoi dire esattamente quando un oggetto viene distrutto);

Per la maggior parte dei programmatori il sistema operativo non è deterministico, il loro allocatore di memoria non è deterministico e la maggior parte dei programmi che scrivono sono concorrenti e, quindi, intrinsecamente non deterministici. L'aggiunta del vincolo che un distruttore viene chiamato esattamente alla fine dell'ambito piuttosto che leggermente prima o leggermente dopo non è un vantaggio pratico significativo per la stragrande maggioranza dei programmatori.

  1. nei linguaggi che usano GC devi spesso fare una gestione manuale delle risorse (vedi la chiusura dei file in Java, per esempio), che in parte vanifica lo scopo di GC ed è anche soggetto a errori;

Vedi usingin C # e usein F #.

  1. la memoria heap può anche (molto elegantemente, imo) essere legata all'ambito (vedi std :: shared_ptr in C ++).

In altre parole, potresti prendere l'heap che è una soluzione di uso generale e cambiarlo per funzionare solo in un caso specifico che sta seriamente limitando. Questo è vero, certo, ma inutile.

Perché SBMM non è più ampiamente utilizzato? Quali sono i suoi svantaggi?

SBMM limita ciò che puoi fare:

  1. SBMM crea il problema funarg verso l' alto con chiusure lessicali di prima classe, motivo per cui le chiusure sono popolari e facili da usare in linguaggi come C # ma rare e complicate in C ++. Si noti che esiste una tendenza generale verso l'uso di costrutti funzionali nella programmazione.

  2. SBMM richiede i distruttori e impediscono le chiamate in coda aggiungendo più lavoro da fare prima che una funzione possa tornare. Le chiamate di coda sono utili per macchine a stati estensibili e sono fornite da cose come .NET.

  3. Alcune strutture di dati e algoritmi sono notoriamente difficili da implementare usando SBMM. Praticamente ovunque quei cicli si verificano naturalmente. In particolare algoritmi grafici. Alla fine finisci per scrivere il tuo GC.

  4. La programmazione concorrente è più difficile perché il flusso di controllo e, quindi, la durata degli oggetti sono intrinsecamente non deterministici qui. Le soluzioni pratiche nei sistemi di trasmissione dei messaggi tendono ad essere una copia profonda dei messaggi e l'uso di una durata eccessivamente lunga.

  5. SBMM mantiene gli oggetti in vita fino alla fine del loro ambito nel codice sorgente, che è spesso più lungo del necessario e può essere molto più lungo del necessario. Ciò aumenta la quantità di immondizia galleggiante (oggetti non raggiungibili in attesa di essere riciclati). Al contrario, il tracciamento della garbage collection tende a liberare oggetti subito dopo la scomparsa dell'ultimo riferimento ad essi, che può essere molto prima. Vedi Miti sulla gestione della memoria: prontezza .

SBMM è così limitante che i programmatori hanno bisogno di una via di fuga per situazioni in cui non è possibile nidificare le vite. In C ++, shared_ptroffre una via di fuga ma può essere ~ 10 volte più lenta della traccia della garbage collection . Quindi usare SBMM invece di GC metterebbe la maggior parte delle persone a piede sbagliato la maggior parte delle volte. Ciò non significa, tuttavia, che sia inutile. SBMM ha ancora valore nel contesto dei sistemi e della programmazione integrata in cui le risorse sono limitate.

FWIW potresti voler dare un'occhiata a Forth e Ada e leggere il lavoro di Nicolas Wirth.


1
Se dici quali bit posso essere in grado di elaborare o citare articoli.
Jon Harrop,

2
Quanto è rilevante essere 10 volte più lento in alcuni rari casi d'uso anziché essere onnipresente in tutti i casi d'uso? C ++ ha unique_ptr e per la maggior parte è sufficiente. Accanto a questo, invece di attaccare RAII tramite C ++ (un linguaggio che molti amano odiare per essere un linguaggio arcaico), se hai intenzione di attaccare RAII attraverso attaccare una lingua, prova un fratello più giovane della famiglia RAII, Rust per esempio. Rust fondamentalmente fa tutto ciò che C ++ ha sbagliato mentre fa la maggior parte delle cose che anche C ++ ha ragione. Ulteriore "utilizzo" ti dà un numero molto limitato di casi d'uso e ignora la composizione.
user1703394

2
"Quanto è rilevante essere 10 volte più lento in alcuni rari casi d'uso anziché essere onnipresente in tutti i casi d'uso?". Innanzitutto, questo è un argomento circolare: shared_ptrè raro solo in C ++ perché è così lento. In secondo luogo, si tratta di un confronto tra mele e arance (come già indicato nell'articolo che ho citato) perché shared_ptrè molte volte più lento di un GC di produzione. In terzo luogo, i GC non sono onnipresenti e vengono evitati in software come LMax e il motore FIX di Rapid Addition.
Jon Harrop,

1
@Jon Harrop, se non mi hai illuminato, per favore. Quale ricetta magica hai usato in oltre 30 anni per mitigare gli effetti transitivi dell'utilizzo di risorse profonde? Senza una ricetta così magica dopo oltre 30 anni, potrei solo concludere che devi aver attribuito erroneamente di essere stato morso da esso ad altre cause.
user1703394,

1
@Jon Harrop, shared_ptr non è raro perché è lento, è raro perché in un sistema progettato in modo decente la necessità di "proprietà condivisa" è rara.
user1703394,

4

Guardando un indice di popolarità come TIOBE (che è discutibile, ovviamente, ma suppongo che per il tuo tipo di domanda sia giusto usarlo), vedi prima che circa il 50% dei primi 20 sono "linguaggi di scripting" o "dialetti SQL ", dove la" facilità d'uso "e i mezzi di astrazione hanno un'importanza molto maggiore del comportamento deterministico. Dalle restanti lingue "compilate", ci sono circa il 50% delle lingue con SBMM e ~ 50% senza. Quindi, quando togli i linguaggi di scripting dal tuo calcolo, direi che la tua ipotesi è sbagliata, tra i linguaggi compilati quelli con SBMM sono popolari come quelli senza.


1
In che modo la "facilità d'uso" è diversa dal determinismo? Un linguaggio deterministico non dovrebbe essere considerato più facile da usare di un linguaggio non deterministico?
Philipp

2
@Philipp Solo ciò che è deterministico o che in realtà non conta. Il tempo di vita dell'oggetto non ha importanza da solo (sebbene C ++ e gli amici leghino molte cose che contano per obiettare il tempo di vita, perché possono farlo). Quando un oggetto non raggiungibile viene liberato, non importa perché, per definizione, non lo si utilizza più.

Inoltre, vari "linguaggi di scripting" come Perl e Python usano anche il conteggio dei riferimenti come mezzo principale per la gestione della memoria.
Philipp

1
@Philipp Almeno nel mondo Python, questo è considerato un dettaglio di implementazione di CPython, non una proprietà del linguaggio (e praticamente ogni altra implementazione evita il conteggio). Inoltre, direi che il conteggio dei riferimenti privi di esclusione con il ciclo di backup GC non si qualifica come SBMM o RAII. In effetti, sarebbe difficile trovare sostenitori di RAII che considerano questo stile di gestione della memoria paragonabile a RAII (soprattutto perché non lo è, cicli ovunque possono impedire la rapida dislocazione in qualsiasi altra parte del programma).

3

Un grande vantaggio di un sistema GC che nessuno ha ancora menzionato è che un riferimento in un sistema GC è garantito per mantenere la sua identità finché esiste . Se si chiama IDisposable.Dispose(.NET) o AutoCloseable.Close(Java) su un oggetto mentre esistono copie del riferimento, tali copie continueranno a fare riferimento allo stesso oggetto. L'oggetto non sarà più utile per nulla, ma i tentativi di utilizzo avranno un comportamento prevedibile controllato dall'oggetto stesso. Al contrario, in C ++, se il codice chiama deleteun oggetto e successivamente tenta di usarlo, l'intero stato del sistema diventa totalmente indefinito.

Un'altra cosa importante da notare è che la gestione della memoria basata sull'ambito funziona molto bene per oggetti con proprietà chiaramente definite. Funziona molto meno bene, e talvolta addirittura male, con oggetti che non hanno una proprietà definita. In generale, gli oggetti mutabili dovrebbero avere proprietari, mentre gli oggetti immutabili non ne hanno bisogno, ma c'è una ruga: è molto comune che il codice usi un'istanza di tipi mutabili per contenere dati immutabili, garantendo che nessun riferimento sarà esposto a codice che potrebbe mutare l'istanza. In tale scenario, le istanze della classe mutabile potrebbero essere condivise tra più oggetti immutabili e quindi non avere una chiara proprietà.


4
La proprietà a cui fai riferimento nella prima metà è la sicurezza della memoria. Mentre un GC è un modo molto semplice per raggiungere la sicurezza della memoria, un GC non è necessario: guarda Rust per un esempio ben fatto.

@delnan: quando visualizzo rust-lang.org, il mio browser non riesce a navigare utilmente da lì; dove dovrei cercare maggiori informazioni? La mia impressione è che la sicurezza della memoria senza GC imponga alcune restrizioni alle strutture di dati che potrebbero non adattarsi bene a tutto ciò che un'applicazione potrebbe dover fare, ma sarei felice di essere smentito.
supercat

1
Non conosco un singolo (o anche un piccolo insieme di) buoni riferimenti per questo; la mia conoscenza di Rust si è accumulata nel giro di un anno o due dalla lettura della mailing list (e di tutte le cose collegate alle e-mail, inclusi vari tutorial, post di blog sulla progettazione della lingua, problemi di github, ThisWeekInRust e altro). Per rispondere brevemente alla tua impressione: Sì, ogni costrutto sicuro (necessariamente) impone restrizioni, ma praticamente per qualsiasi pezzo di codice sicuro per la memoria, esiste un costrutto sicuro appropriato o può essere scritto. Quelle di gran lunga più comuni esistono già in lingua e stdlib, tutte le altre possono essere scritte nel codice utente.

@delnan: Rust richiede aggiornamenti interbloccati per i contatori di riferimento o ha altri mezzi per gestire oggetti immutabili (o oggetti mutabili immutabilmente avvolti) che non hanno una proprietà definita? Rust ha un concetto di puntatore "proprietario dell'oggetto" e "non proprietario"? Ricordo un documento sui puntatori "Xonor" che discuteva l'idea di oggetti con un unico riferimento che li "possiede" e altri riferimenti che non lo fanno; quando il riferimento "proprietario" non rientra nel campo di applicazione, tutti i riferimenti non proprietari diventerebbero riferimenti a oggetti morti e sarebbero identificabili come tali ...
supercat

1
Non credo che i commenti di Stack Exchange siano il mezzo giusto per fare un tour attraverso una lingua. Se sei ancora interessato, puoi andare direttamente alla fonte (#rust IRC, mailing list rust-dev, ecc.) E / o cercarmi in una chat room (dovresti essere in grado di crearne uno).

-2

Prima di tutto, è molto importante rendersi conto che equiparare RAII a SBMM. o anche a SBRM. Una delle qualità più essenziali (e meno conosciute o meno apprezzate) di RAII è il fatto che rende "essere una risorsa" una proprietà che NON è transitiva alla composizione.

Il seguente post sul blog discute questo importante aspetto di RAII e lo contrappone all'ampliamento delle risorse nei linguaggi GCed che usano GC non deterministico.

http://minorfs.wordpress.com/2011/04/29/why-garbage-collection-is-anti-productive/

È importante notare che mentre RAII viene utilizzato principalmente in C ++, Python (finalmente la versione non basata su VM) ha distruttori e GC deterministici che consentono l'utilizzo di RAII insieme a GC. Meglio di entrambi i mondi se fosse.


1
-1 Questo è uno dei peggiori articoli che abbia mai letto.
Jon Harrop,

1
Il problema nelle lingue non è che supportano GC, ma che abbandonano RAII. Non c'è motivo per cui un linguaggio / framework non dovrebbe essere in grado di supportare entrambi.
supercat

1
@Jon Harrop, potresti approfondire. Delle affermazioni fatte nell'articolo, c'è anche una delle prime 3 affermazioni che non regge? Penso che potresti non essere d'accordo sull'affermazione sulla produttività, ma le altre 3 affermazioni sono assolutamente valide. Soprattutto il primo sulla transitività dell'essere una risorsa.
user1703394

2
@ user1703394: In primo luogo, l'intero articolo è basato su un "linguaggio GCed" di paglia quando, in realtà, non ha nulla a che fare con la raccolta dei rifiuti. In secondo luogo, incolpa la raccolta dei rifiuti quando, in realtà, i difetti risiedono nella programmazione orientata agli oggetti. Infine, la sua tesi è in ritardo di 10 anni. La stragrande maggioranza dei programmatori ha già ammodernato i linguaggi raccolti con immondizia proprio perché offrono una produttività molto più elevata.
Jon Harrop,

1
I suoi esempi concreti (RAM, handle di file aperti, blocchi, thread) sono abbastanza significativi. Ho difficoltà a ricordare l'ultima volta che ho dovuto scrivere un codice che trattasse direttamente di uno di questi. Con la RAM, il GC automatizza tutto. Con gli handle di file scrivo codice come il punto in File.ReadLines file |> Seq.lengthcui le astrazioni gestiscono la chiusura per me. Blocchi e thread che ho sostituito con .NET Taske F # MailboxProcessor. Tutta questa faccenda "Abbiamo fatto esplodere la quantità di gestione manuale delle risorse" non ha che senso.
Jon Harrop,
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.