Sebbene la maggior parte della mia esperienza sia con la programmazione non GC, non credo che GC o non GC siano universalmente superiori.
Ciò che dirò, tuttavia, è che i sovraccarichi della memoria commerciale per la velocità o per altre risorse sono abbastanza comuni, anche per i linguaggi non GC.
Alcuni esempi ...
Un array ingenuo di dimensioni ridimensionabili cresce con incrementi fissi. Quando si inserisce un numero elevato di elementi, questo si traduce in prestazioni O (n ^ 2) a causa delle operazioni di riallocazione e copia sull'array O (n). Un'alternativa più veloce è il "raddoppiamento dell'array" - più precisamente, crescendo di un fattore fisso. Ciò fornisce prestazioni O (n) ammortizzate per una lunga sequenza di inserti. Il costo è che, invece di avere un overhead di memoria massimo fisso, il sovraccarico massimo è proporzionale al numero di elementi nell'array.
EDIT - errore stupido sopra - array fornisce O (1) per ogni inserto, ma rigorosamente O (n) per una serie di n inserimenti, a causa di come "ammortizzato" è definito.
Il raddoppiamento delle matrici non si applica solo a tipi vettoriali / array ridimensionabili semplici, ma anche a tipi con matrici resettabili sottostanti - le più ovvie sono le tabelle hash. Senza questo overhead di memoria proporzionale, non è possibile ottenere i tempi di inserimento / eliminazione ammortizzati di O (1) per hash ridimensionabili. Ciò vale indipendentemente dal fatto che venga utilizzata la garbage collection.
Allo stesso modo, con alberi B e alberi B + (comunemente usati per gli indici del database), ogni nodo è garantito almeno per metà - con l'eccezione della radice, che può anche essere completamente vuota. Queste strutture di dati sono più probabilmente utilizzate sul disco che nella memoria principale, ma in entrambi i casi hanno un sovraccarico di memoria / storage previsto che cresce in proporzione al numero di articoli.
Con algoritmi di garbage collection, ci sono anche compromessi tra memoria e prestazioni. In alcuni casi questo può essere visto come un ulteriore livello in cima alle strutture dati, aggiungendo uno strato aggiuntivo di rifiuti, ma questa non è una regola generale - non so quale peso pesare su ciascun caso.
Nelle lingue non gestite, è necessario un altro meccanismo per tenere traccia della memoria che deve essere comunque liberata. A volte, questo è praticamente gratis nella struttura dati stessa - ad es. in un albero binario, sono necessari i collegamenti secondari indipendentemente dal fatto che vengano utilizzati per un'operazione di eliminazione di tutti gli elementi o meno. Ma altre volte anche questo può comportare un compromesso tra overhead e prestazioni extra della memoria. Ad esempio, i "pool di memoria" vengono spesso utilizzati per scambiare i costi generali della memoria (l'intero pool rimane allocato, anche se relativamente pochi articoli all'interno del pool sono in uso in un determinato momento) per una più veloce e più conveniente libera da tutti gli elementi contemporaneamente.
Il mio punto di partenza è, immagino, una variazione su "evitare l'ottimizzazione prematura". Non preoccuparti troppo dei sovraccarichi di memoria se non sai che causeranno un problema. E sappi che quando si riducono le spese generali di memoria, è probabile che si paghi con aumenti ad altre spese generali, quindi fare prima e dopo le misurazioni di più di un semplice utilizzo della memoria.
Potrebbe sembrare che sia troppo tardi per pensare al cambio di lingua quando si riscontra un problema con i sovraccarichi della memoria di garbage collection, ma ci sono cose che si possono fare senza riscrivere l'intero sistema. Ad esempio, quando si codifica in C # per .NET, un'opzione è di reimplementare una struttura di dati critica utilizzando un C ++ non gestito.