5

La maggior parte dei programmatori concorda sul fatto che la garbage collection è una grande cosa, e nella maggior parte delle applicazioni vale la spesa. Tuttavia, la mia osservazione personale è che la gestione della memoria per la maggior parte degli oggetti è banale, e forse il 10% -20% di loro spiega la necessità di kludges come il conteggio dei riferimenti e schemi di gestione della memoria davvero complicati in generale. Mi sembra che uno possa ottenere tutti i benefici della garbage collection con solo una piccola parte dell'overhead eliminando in modo conservativo grandi oggetti manualmente dove la vita dell'oggetto è ovvia e lasciando che il GC raccolga il resto, assumendo che l'implementazione GC supporti tale cosa . Ciò consentirebbe al GC di funzionare molto meno frequentemente e di consumare meno memoria in eccesso, evitando allo stesso tempo casi che sono difficili da gestire manualmente. Ancora più interessante sarebbe se il compilatore inserisce automaticamente le istruzioni di eliminazione deterministici in cui vite erano evidenti:Una regola 90/10 per la gestione della memoria?

int myFunc() { 
    Foo[] foo = new Foo[arbitraryNumber]; // May be too big to stack allocate. 
    // do stuff such that the compiler can prove foo doesn't escape. 
    // foo is obviously no longer needed, can be automatically deleted here. 
    return someInteger; 
} 

Naturalmente, questo non potrebbe funzionare bene con un GC copia, ma per il bene di questo post supponiamo nostro ISN GC copiare. Perché tali schemi di gestione della memoria ibrida sono apparentemente così rari nei linguaggi di programmazione tradizionali?

+1

Le vaghe affermazioni sull'osservazione personale non aiutano gli altri programmatori. Cosa hai * misurato *? –

risposta

3

Perché questo caso è troppo raro. Quasi nessun metodo è isolato. Tutti accettano oggetti dall'esterno o creano oggetti e li distribuiscono.

Un metodo che non accede a nessun campo, non ha parametri e non restituisce qualcosa non può fare nulla.

Invece, i GC si concentrano sul caso più comune (il 90%) e cercano di mantenere sotto controllo il 90% (gli oggetti temporanei di breve durata). Ciò significa che nel caso comune, hai meno oggetti da controllare e il resto non ha importanza. Successivamente, si utilizza una scansione incrementale (in modo da poter eseguire in piccoli sprint che interrompono solo per un breve momento).

Una volta ho provato a elaborare un algoritmo GC migliore e ho fallito miseramente. Usano un approccio che rasenta l'arcano. Il documento su Java 5 GC Performance Tuning dovrebbe darti qualche idea. E c'è ovviamente lo GC article in Wikipedia.

Cosa si riduce a: L'utilizzo di un GC può anche essere più rapido di una tradizionale allocazione di memoria e di uno schema di liberazione. Pensa al classico algoritmo che individua qualsiasi oggetto raggiungibile e lo copia in un nuovo luogo. Se ti sei appena dimenticato di un sacco di oggetti (ad esempio, il 90% di tutti gli oggetti allocati), questo algoritmo deve solo controllare il resto (10%). Tutto quello che non può raggiungere, non importa quanto possa essere, non avrà importanza. Ora si può dire che la copia è costosa ma a) questo non è vero; oggi una CPU desktop media può copiare 40 MB in meno di 100 ms eb) la copia ti proteggerà contro la frammentazione quindi è una cosa buona.

+0

Ok, errore stupido. Fondamentalmente insinuavo una funzione pura, ma le funzioni pure dovrebbero restituire qualcosa. Fisso. – dsimcha

+0

Controlla tutto il codice sorgente che hai scritto nella tua vita. Dubito che troverete molti metodi che fanno qualcosa di utile * e * non funziona su oggetti da un ambito esterno. Inoltre, come ho spiegato, gli oggetti live sono costosi, gli oggetti morti sono gratuiti. –

+1

Nella mia esperienza, gli sviluppatori di app non sono nemmeno a conoscenza della metà delle allocazioni che effettuano.Forse non eri a conoscenza di questi banali casse della spazzatura perché non ti rendevi conto di quanto spazzatura hai fatto. – Aaron

1

Una breve nota sul "ovviamente non è più necessaria": non è così facile;)

[...] 
Foo() { 
    someGlobalList.add(this); 
} 
[...] 

A parte questo, la vostra idea di essere in grado di eliminare manualmente grandi thingies è imo una buona. A quanto ho capito, è almeno in parte implementato nelle lingue moderne (ad esempio using in C#, che purtroppo in realtà non è gratuito). Tuttavia, non nella misura che desideri.

+0

sì, ma si consideri il caso di "stringa s1, s2, s3; ... s1 + s2 + s3". La memoria allocata durante la concatenazione di s1 e s2 sarà spazzatura prima della fine di questa espressione, garantita. - La mia comprensione è che vari Lisps hanno avuto questo tipo di ottimizzazioni. – Aaron

1

Quello che descrivi come istruzioni di eliminazione deterministica per gli oggetti di non escape le moderne implementazioni di GC funzionano in modo abbastanza efficace. La maggior parte degli oggetti allocati sono allocati dai pool e cancellati in modo molto efficiente all'uscita del metodo - molto pochi finiscono nell'heap GC più lento.

Questo in effetti produce lo stesso effetto descritto con un minore intervento del programmatore.E poiché un garbage collector è automatico consente meno possibilità di errore umano (cosa succede se hai cancellato qualcosa per cui era ancora conservato un riferimento).

0

Se si sta creando un oggetto che si prevede verrà utilizzato solo nel contesto di una chiamata di metodo alla volta e che non ha finalizzazione, quindi si consiglia di utilizzare un tipo di valore anziché un tipo di riferimento nelle lingue che fai questa distinzione Ad esempio, in C# potresti dichiarare la tua entità come struct anziché class. Quindi le istanze locali del metodo a vita breve verranno allocate nello stack anziché nell'heap e saranno implicitamente deallocate nel momento in cui viene restituito il metodo.

E questa tecnica può andare ancora oltre la tua idea originale, perché la struttura può essere passata ad altri metodi senza preoccuparsi di interrompere l'analisi della durata.

Per un array, come nella domanda, è possibile utilizzare il comando stackalloc per ottenere questo effetto.

+0

Stavo pensando che questi odori siano sospettosamente simili agli oggetti allocati sullo stack rispetto all'heap :) –

2

"La maggior parte dei programmatori concorda sul fatto che la raccolta dei dati inutili è una grande cosa e nella maggior parte delle applicazioni vale la pena di essere spesa."

generalizzazioni ...

+0

In secondo luogo questa affermazione. –

+0

Concordo sul fatto che la garbage collection è una grande cosa, e nella maggior parte delle applicazioni vale il sovraccarico. ;-) – teedyay

+0

Non fraintendetemi, sia GC che non GC hanno il loro posto IMO. –

0

Questo sembra essere esattamente come D gestisce la memoria. I suoi rifiuti vengono raccolti mentre consentono di eliminare in modo specifico gli oggetti quando si desidera, o addirittura di evitare il GC tutti insieme a favore di malloc/free. La parola chiave scoped sembra fare quello che vuoi sullo stack, anche se non sono sicuro se alloca effettivamente l'oggetto nello stack o nell'heap.

0

Mentre ci possono essere alcuni vantaggi nel combinare la pulizia manuale con la garbage collection, c'è un vantaggio sostanziale per avere il garbage collector che continua a gestire gli oggetti tra il momento in cui non sono più necessari e il tempo in cui è possibile mostrare di non avere superstiti riferimenti radicati. Tra l'altro, in un sistema non GC, è spesso molto difficile dimostrare quando si elimina un oggetto che non esiste alcun modo in cui un riferimento possa ancora esistere. Se un oggetto viene eliminato mentre esiste ancora un riferimento, un tentativo di utilizzare quel riferimento potrebbe causare un comportamento arbitrario non definito; difendersi da tale pericolo senza un GC è in generale difficile. Al contrario, se uno invalida gli oggetti quando ne viene fatto uno, ma lascia il riutilizzo della memoria precedentemente occupata al GC, si può garantire che un tentativo di usare l'oggetto invalidato fallirà in modo prevedibile.

Per inciso, se stavo progettando un "micro quadro", avrei aree heap separate per oggetti mutevoli e immutabili. La garbage collection generazionale funziona meglio se il codice può stabilire se un oggetto è stato scritto da quando gen0 o gen1 sono stati raccolti. Fare una tale determinazione con oggetti mutabili è molto più difficile che con quelli immutabili. D'altra parte, la gestione manuale della vita utile degli oggetti mutabili è generalmente più semplice della gestione della vita utile degli oggetti immutabili, poiché il primo dovrebbe generalmente avere un "proprietario" chiaro, mentre il secondo generalmente non lo farà.