Por que linguagens como C e C ++ não possuem coleta de lixo, enquanto Java o faz? [fechadas]

54

Bem, eu sei que existem coisas como malloc / free para C, e new / using-a-destructor para gerenciamento de memória em C ++, mas fiquei me perguntando por que não há "novas atualizações" para esses idiomas que permitem o usuário tem a opção de gerenciar manualmente a memória ou o sistema faz isso automaticamente (coleta de lixo)?

Algo de uma questão newb-ish, mas apenas no CS há cerca de um ano.

    
por Dark Templar 14.10.2011 / 21:41
fonte

16 respostas

68

Coleta de lixo requer estruturas de dados para rastrear alocações e / ou contagem de referência. Isso cria uma sobrecarga na memória, no desempenho e na complexidade da linguagem. C ++ é projetado para ser "próximo ao metal", em outras palavras, leva o lado de maior desempenho dos recursos de conveniência versus tradeoff. Outras linguagens fazem essa troca de maneira diferente. Essa é uma das considerações na escolha de uma linguagem, a ênfase que você prefere.

Dito isso, há muitos esquemas para contagem de referência em C ++ que são relativamente leves e de alto desempenho, mas estão em bibliotecas, tanto comerciais como de código aberto, em vez de parte da própria linguagem. A contagem de referência para gerenciar o tempo de vida do objeto não é o mesmo que a coleta de lixo, mas trata de muitos dos mesmos tipos de problemas e é mais adequada à abordagem básica do C ++.

    
por 06.09.2015 / 06:55
fonte
43

Estritamente falando, não há gerenciamento de memória na linguagem C. malloc () e free () não são palavras-chave no idioma, mas apenas funções que são chamadas de uma biblioteca. Essa distinção pode ser pedante agora, porque malloc () e free () fazem parte da biblioteca padrão C, e serão fornecidos por qualquer implementação compatível com o padrão C, mas isso nem sempre foi verdade no passado.

Por que você deseja um idioma sem padrão para gerenciamento de memória? Isso remonta às origens do C como 'montagem portátil'. Existem muitos casos de hardware e algoritmos que podem se beneficiar ou até mesmo exigir técnicas especializadas de gerenciamento de memória. Até onde sei, não há como desabilitar completamente o gerenciamento de memória nativa do Java e substituí-lo pelo seu próprio. Isso simplesmente não é aceitável em algumas situações de alto desempenho / recursos mínimos. C oferece flexibilidade quase completa para escolher exatamente qual infraestrutura seu programa vai usar. O preço pago é que a linguagem C oferece muito pouca ajuda para escrever códigos corretos e livres de bugs.

    
por 09.10.2011 / 00:12
fonte
31

A verdadeira resposta é que a única maneira de criar um mecanismo de coleta de lixo seguro e eficiente é ter suporte em nível de idioma para referências opacas. (Ou, inversamente, falta de suporte no nível da linguagem para manipulação direta da memória.)

Java e C # podem fazer isso porque eles têm tipos de referência especiais que não podem ser manipulados. Isso dá ao runtime 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 , de modo que é completamente um arenque vermelho. Os GCs modernos usam a coleção geracional, onde novas alocações são tratadas essencialmente da mesma maneira que as alocações de pilha estão em uma linguagem como C ++, e então periodicamente quaisquer objetos recém-alocados que ainda estão vivos são movidos para um espaço "sobrevivente" separado e uma geração inteira de objetos é desalocada de uma só vez.

Essa abordagem tem vantagens e desvantagens: a vantagem é que as alocações de heap em uma linguagem que suporta GC são tão rápidas quanto as alocações de pilha em uma linguagem que não suporta GC, e a desvantagem é que objetos que precisam executar a limpeza antes de serem destruídos requerem um mecanismo separado (por exemplo, a palavra-chave using do C #) ou então seu 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 de idioma para uma classe especial de referências. C não tem esse suporte de idioma e nunca terá; porque o C ++ tem sobrecarga de operador, ele poderia emular um tipo de ponteiro GC, embora isso devesse ser feito com cuidado. Na verdade, quando a Microsoft inventou seu dialeto de C ++ que seria executado sob o CLR (o tempo de execução do .NET), eles precisavam inventar uma nova sintaxe para "referências em estilo C #" (por exemplo, Foo^ ) para distingui-las de "C ++ - referências de estilo "(por exemplo, Foo& ).

O que o C ++ tem, e o que é regularmente usado pelos programadores de C ++, é ponteiros inteligentes , que são realmente apenas um mecanismo de contagem de referências. Eu não consideraria a contagem de referência como "verdadeira" GC, mas ela fornece muitos dos mesmos benefícios, ao custo de desempenho mais lento que o gerenciamento de memória manual ou 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 permitiu que ela fosse compatível com C e ainda oferecesse alternativas boas o suficiente para a maioria das finalidades, e Java e C # fizeram uma escolha diferente que é incompatível com C mas também é boa o suficiente mais propósitos. Infelizmente, não existe uma bala de prata, mas estar familiarizado com as diferentes opções lá fora irá ajudá-lo a escolher o correto para qualquer programa que você está tentando construir.

    
por 07.09.2015 / 01:02
fonte
26

Porque, ao usar o poder do C ++, não há necessidade.

Herb Sutter: " Eu não escrevi delete em anos. "

veja Escrevendo o código C ++ moderno: como o C ++ evoluiu ao longo dos anos 21:10

Pode surpreender muitos programadores experientes em C ++.

    
por 08.10.2011 / 23:27
fonte
15

"Tudo" é um coletor de lixo é um processo que é executado periodicamente verificando se há algum objeto não referenciado na memória e se há exclui-los. (Sim, eu sei que isso é uma simplificação grosseira). Esta não é uma propriedade da linguagem, mas o framework.

Existem coletores de lixo escritos para C e C ++ - este por exemplo.

Um dos motivos pelos quais um não foi "adicionado" ao idioma pode ser devido ao grande volume de código existente que nunca o utilizaria, pois usam seu próprio código para gerenciar a memória. Outra razão pode ser que os tipos de aplicativos escritos em C e C ++ não precisam da sobrecarga associada a um processo de coleta de lixo.

    
por 08.10.2011 / 23:09
fonte
12

O C foi projetado em uma época em que a coleta de lixo era apenas uma opção. Ele também era destinado a usos em que a coleta de lixo geralmente não funcionava - bare metal, ambientes em tempo real com memória mínima e suporte mínimo em tempo de execução. Lembre-se que C foi a linguagem de implementação para o primeiro unix, que rodava em um pdp-11 com 64 * K * bytes de memória. C ++ era originalmente uma extensão para C - a escolha já havia sido feita, e é muito difícil enxertar a coleta de lixo em um idioma existente. É o tipo de coisa que tem que ser construída a partir do térreo.

    
por 09.10.2011 / 00:19
fonte
9

Eu não tenho as citações exatas, mas tanto Bjarne quanto Herb Sutter dizem algo nos moldes:

C++ doesn't need a garbage collector, because it has no garbage.

No C ++ moderno, você usa ponteiros inteligentes e, portanto, não tem lixo.

    
por 08.10.2011 / 22:56
fonte
8

Você pergunta por que esses idiomas não foram atualizados para incluir um coletor de lixo opcional.

O problema com a coleta de lixo opcional é que você não pode misturar código que usa os diferentes modelos. Ou seja, se eu escrever um código que pressupõe que você esteja usando um coletor de lixo, não será possível usá-lo em seu programa que tenha a coleta de lixo desativada. Se você fizer isso, vai vazar em todos os lugares.

    
por 09.10.2011 / 06:53
fonte
6

Você pode imaginar escrever um manipulador de dispositivo em um idioma com coleta de lixo? Quantos bits puderam passar pela linha enquanto o GC estava sendo executado?

Ou um sistema operacional? Como você poderia iniciar a coleta de lixo antes mesmo de iniciar o kernel?

C é projetado para baixo nível próximo às tarefas de hardware. O problema? é uma linguagem tão boa que é uma boa escolha para muitas tarefas de nível superior também. Os czares do idioma estão cientes desses usos, mas eles precisam dar suporte aos requisitos dos drivers de dispositivos, códigos embutidos e sistemas operacionais como uma prioridade.

    
por 14.10.2011 / 10:42
fonte
6

A resposta curta e chata a esta pergunta é que é preciso haver uma linguagem não coletada para as pessoas que escrevem os coletores de lixo. Não é conceitualmente fácil ter uma linguagem que, ao mesmo tempo, permita um controle muito preciso sobre o layout da memória e tenha um GC sendo executado na parte superior.

A outra questão é por que C e C ++ não têm coletores de lixo. Bem, eu sei que o C ++ tem alguns deles por aí, mas eles não são realmente populares porque eles são forçados a lidar com uma linguagem que não foi projetada para ser escrita em CG, e as pessoas que ainda usam o C ++ em esta idade não é realmente do tipo que perde um GC.

Além disso, em vez de adicionar o GC a uma antiga linguagem não-compilada, é realmente mais fácil criar um novo idioma que tenha a mesma sintaxe ao mesmo tempo que suporta um GC. Java e C # são bons exemplos disso.

    
por 04.10.2014 / 21:51
fonte
5

Existem vários problemas, incluindo ...

  • Embora o GC tenha sido inventado antes de C ++ e, possivelmente, antes de C, C e C ++ foram implementados antes que os GCs fossem amplamente aceitos como práticos.
  • Você não pode implementar facilmente uma linguagem e uma plataforma de GC sem uma linguagem subjacente não relacionada a GC.
  • Embora o GC seja comprovadamente mais eficiente que o não-GC para códigos de aplicativos típicos desenvolvidos em escalas de tempo típicas etc., há problemas em que mais esforço de desenvolvimento é um bom compromisso e o gerenciamento de memória especializado supera um GC de uso geral. Além disso, o C ++ é normalmente mais eficiente do que a maioria das linguagens GC, mesmo sem nenhum esforço extra de desenvolvimento.
  • O GC não é universalmente mais seguro que o RAII no estilo C ++. O RAII permite que recursos além da memória sejam limpos automaticamente, basicamente porque suporta destruidores confiáveis e oportunos. Estes não podem ser combinados com métodos GC convencionais devido a problemas com ciclos de referência.
  • As linguagens GC têm seus próprios tipos característicos de vazamentos de memória, particularmente relacionados à memória que nunca será usada novamente, mas onde existiam referências existentes que nunca foram anuladas ou substituídas. A necessidade de fazer isso explicitamente não é diferente em princípio do que a necessidade de delete ou free explicitamente. A abordagem do GC ainda tem uma vantagem - sem referências pendentes - e a análise estática pode captar alguns casos, mas, novamente, não há uma solução perfeita para todos os casos.

Basicamente, em parte, é sobre a idade das línguas, mas sempre haverá um lugar para as linguagens que não são GC, mesmo que seja um local um pouco estranho. E, falando sério, em C ++, a falta de GC não é grande coisa - sua memória é gerenciada de maneira diferente, mas não é gerenciada.

Microsofts gerenciados O C ++ tem pelo menos alguma capacidade de misturar GC e não-GC no mesmo aplicativo, permitindo uma combinação das vantagens de cada um, mas não tenho experiência para dizer o quanto isso funciona na prática.

Links de prostituição para as respostas relacionadas ...

por 23.05.2017 / 13:33
fonte
4

A coleta de lixo é fundamentalmente incompatível com uma linguagem de sistemas usada para desenvolver drivers para hardware compatível com DMA.

É inteiramente possível que o único ponteiro para um objeto seja armazenado em um registrador de hardware em algum periférico. Uma vez que o coletor de lixo não saberia sobre isso, ele pensaria que o objeto estava inacessível e o coletaria.

Esse argumento é duplo para compactar o GC. Mesmo se você tivesse o cuidado de manter referências na memória para objetos usados por periféricos de hardware, quando o GC realocasse o objeto, ele não saberia como atualizar o ponteiro contido no registro de configuração periférico.

Então, agora você precisaria de uma mistura de buffers de DMA imóveis e objetos gerenciados por GC, o que significa que você tem todas as desvantagens de ambos.

    
por 15.10.2011 / 05:59
fonte
3

Porque, C & C ++ são linguagens de nível relativamente baixo destinadas para uso geral, até mesmo, por exemplo, para rodar em um processador de 16 bits com 1MB de memória em um sistema embarcado, que não poderia arcar com o desperdício de memória com o gc.

    
por 09.10.2011 / 06:38
fonte
2

Existem coletores de lixo em C ++ e C. Não sei como isso funciona em C, mas em C ++ você pode aproveitar RTTI para descobrir dinamicamente seu gráfico de objetos e usá-lo para a coleta de lixo.

No meu conhecimento, você não pode escrever Java sem um coletor de lixo. Uma pequena pesquisa apareceu this .

A principal diferença entre Java e C / C ++ é que em C / C ++ a escolha é sempre sua, enquanto que em Java você fica sem opções por design.

    
por 23.05.2017 / 14:40
fonte
2

É um compromisso entre desempenho e segurança.

Não há garantia de que seu lixo será coletado em Java, portanto, ele pode estar ocupando espaço por muito tempo, enquanto a varredura por objetos não referenciados (isto é, lixo) também demora mais do que excluir explicitamente ou liberar um espaço não utilizado. objeto.

A vantagem é que, é claro, é possível construir uma linguagem sem ponteiros ou sem vazamentos de memória, portanto, é mais provável que ocorra um código correto.

Por vezes, pode haver uma ligeira vantagem 'religiosa' nestes debates - esteja avisado!

    
por 09.10.2011 / 21:36
fonte
1

Aqui está uma lista de problemas inerentes do GC, que o tornam inutilizável em uma linguagem de sistema como o C:

  • O GC precisa ser executado abaixo do nível do código cujos objetos ele gerencia. Simplesmente não existe tal nível em um kernel.

  • Um GC precisa interromper o código gerenciado de tempos em tempos. Agora pense sobre o que aconteceria se fizesse isso com seu kernel. Todo o processamento em sua máquina seria interrompido por, digamos, um milissegundo, enquanto o GC verifica todas as alocações de memória existentes. Isso eliminaria todas as tentativas de criar sistemas que operam sob rigorosos requisitos em tempo real.

  • Um GC precisa ser capaz de distinguir entre ponteiros e não ponteiros. Isto é, deve ser capaz de olhar para todos os objetos de memória existentes, e ser capaz de produzir uma lista de deslocamentos onde seus ponteiros podem ser encontrados.

    Esta descoberta deve ser perfeita: O GC deve ser capaz de perseguir todos os ponteiros que descobre. Se ele fizesse um falso positivo, provavelmente iria falhar. Se ele não descobrir um falso negativo, provavelmente destruirá um objeto que ainda está em uso, causando falha no código gerenciado ou corrompendo silenciosamente seus dados.

    Isso requer absolutamente que as informações de tipo sejam armazenadas em todos os objetos existentes. No entanto, C e C ++ permitem objetos de dados simples que não contêm informações de tipo.

  • O GC é um negócio inerentemente lento. Os programadores que foram socializados com Java podem não perceber isso, mas os programas podem ser mais rápidos quando não são implementados em Java. E um dos fatores que tornam o Java lento é o GC. Isso é o que impede que linguagens gerenciadas, como Java, sejam usadas em supercomputação. Se a sua máquina custa um milhão por ano em consumo de energia, você não quer pagar nem 10% disso pela coleta de lixo.

C e C ++ são idiomas criados para suportar todos os casos de uso possíveis. E, como você pode ver, muitos desses casos de uso são impedidos pela coleta de lixo. Portanto, para oferecer suporte a esses casos de uso, o C / C ++ não pode ser coletado como lixo.

    
por 06.09.2015 / 09:23
fonte