A resposta real é que a única maneira de criar um mecanismo de coleta de lixo eficiente e seguro é ter suporte no nível do idioma para referências opacas. (Ou, inversamente, falta de suporte no nível do idioma para manipulação direta de memória.)
Java e C # podem fazê-lo porque possuem tipos de referência especiais que não podem ser manipulados. Isso dá ao tempo de execução a liberdade de fazer coisas como mover objetos alocados na memória , o que é crucial para uma implementação de GC de alto desempenho.
Para o registro, nenhuma implementação moderna de GC usa contagem de referência , o que é completamente um arenque vermelho. Os GCs modernos usam coleção geracional, onde novas alocações são tratadas essencialmente da mesma maneira que as alocações de pilha em uma linguagem como C ++ e, periodicamente, todos os objetos recém-alocados que ainda estão ativos são movidos para um espaço "sobrevivente" separado e para uma geração inteira de objetos é desalocado de uma só vez.
Essa abordagem tem prós e contras: o lado positivo é que as alocações de heap em um idioma que suporta o GC são tão rápidas quanto as alocações de pilha em um idioma que não suporta o GC, e o lado negativo é que os objetos que precisam executar a limpeza antes de serem destruídos também requerem um mecanismo separado (por exemplo, a using
palavra-chave do C # ) ou o código de limpeza é executado de forma não determinística.
Observe que uma chave para um GC de alto desempenho é que deve haver suporte ao idioma para uma classe especial de referências. C não tem esse suporte de idioma e nunca terá; como o C ++ possui sobrecarga de operador, ele pode emular um tipo de ponteiro de GC, embora isso deva ser feito com cuidado. De fato, quando a Microsoft inventou o dialeto do C ++ que seria executado no CLR (o .NET runtime), eles tiveram que inventar uma nova sintaxe para "referências no estilo C #" (por exemplo Foo^
) para distingui-las de "referências no estilo C ++" (por exemplo Foo&
).
O que o C ++ possui e o que é usado regularmente pelos programadores em C ++ são indicadores inteligentes , que são realmente apenas um mecanismo de contagem de referência. Eu não consideraria a contagem de referência um GC "verdadeiro", mas fornece muitos dos mesmos benefícios, ao custo de desempenho mais lento do que o gerenciamento manual de memória ou o GC verdadeiro, mas com a vantagem da destruição determinística.
No final do dia, a resposta realmente se resume a um recurso de design de linguagem. C fez uma escolha, C ++ fez uma escolha que permitia a compatibilidade com versões anteriores com C, enquanto ainda fornecia alternativas boas o suficiente para a maioria dos propósitos, e Java e C # fizeram uma escolha diferente que é incompatível com C, mas também é boa o suficiente para maioria dos propósitos. Infelizmente, não existe uma bala de prata, mas estar familiarizado com as diferentes opções disponíveis no mercado ajudará você a escolher a correta para qualquer programa que esteja tentando construir no momento.