Bem, eu sei que existem coisas como malloc / free for C e new / using-a-destructor para gerenciamento de memória em C ++, mas eu queria saber por que não existem "novas atualizações" nessas linguagens que permitem ao usuário tem a opção de gerenciar manualmente a memória ou o sistema faz isso automaticamente (coleta de lixo)?
Um pouco de uma pergunta nova, mas só está no CS há cerca de um ano.
garbage-collection
Templários das Trevas
fonte
fonte
Respostas:
A coleta de lixo requer estruturas de dados para rastrear alocações e / ou contagem de referência. Isso cria sobrecarga na memória, desempenho e complexidade do idioma. O C ++ foi projetado para ser "próximo ao metal"; em outras palavras, é o lado de maior desempenho dos recursos de troca versus conveniência. Outros idiomas fazem essa troca de maneira diferente. Essa é uma das considerações na escolha de um idioma, cuja ênfase você prefere.
Dito isso, existem muitos esquemas para contagem de referência em C ++ que são bastante leves e com bom desempenho, mas eles estão em bibliotecas, comerciais e de código aberto, em vez de fazer parte da própria linguagem. A contagem de referência para gerenciar a vida útil do objeto não é a mesma que coleta de lixo, mas trata muitos dos mesmos tipos de problemas e é mais adequada à abordagem básica do C ++.
fonte
A rigor, não há gerenciamento de memória no idioma C. malloc () e free () não são palavras-chave no idioma, mas apenas funções 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 padrão de 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 de C como 'montagem portátil'. Existem muitos casos de hardware e algoritmos que podem se beneficiar ou mesmo exigir técnicas especializadas de gerenciamento de memória. Até onde eu sei, não há como desativar completamente o gerenciamento de memória nativa do Java e substituí-lo pelo seu. Isso simplesmente não é aceitável em algumas situações de alto desempenho / recursos mínimos. C fornece flexibilidade quase completa para escolher exatamente qual infraestrutura seu programa usará. O preço pago é que a linguagem C fornece muito pouca ajuda para escrever código correto e sem erros.
fonte
malloc()
oufree()
. (por exemplo, compiladores MLAP para PIC)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 exemploFoo&
).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.
fonte
std::unique_ptr
é "suporte no nível do idioma para referências opacas"? (Não foi o tipo de suporte que eu quis dizer e também não acho que seja suficiente, a menos que o suporte à manipulação direta de memória também tenha sido removido do C ++.) Mencionei ponteiros inteligentes na minha resposta e considerariastd:unique_ptr
um ponteiro inteligente , como ele realmente faz a contagem de referências, ele suporta apenas os casos especiais em que o número de referências é zero ou um (estd::move
é o mecanismo de atualização da contagem de referências).std::unique_ptr
não possui uma contagem de referência estd::move
não tem nada a ver com referências (portanto, não há desempenho atingido). Eu vejo o seu ponto, porém, comostd::shared_ptr
tem uma contagem de referência que é implicitamente atualizado porstd::move
:)malloc
efree
. Então, sim, um GC pode ser substancialmente mais rápido. (Note que eu disse "pode ser." - é claro que o desempenho exato de cada programa é afetado por muitos fatores)Porque, ao usar o poder do C ++, não há necessidade.
Herb Sutter: " Não escrevo delete há anos. "
consulte Escrevendo código C ++ moderno: como o C ++ evoluiu ao longo dos anos 21:10
Pode surpreender muitos programadores experientes em C ++.
fonte
"Tudo" é um coletor de lixo é um processo que é executado periodicamente, verificando se há algum objeto não referenciado na memória e se ele é excluído. (Sim, eu sei que isso é uma simplificação grosseira). Esta não é uma propriedade da linguagem, mas a estrutura.
Existem coletores de lixo escritos para C e C ++ - este por exemplo.
Uma das razões pelas quais uma pessoa não foi "adicionada" ao idioma pode ser o grande volume de código existente que nunca o utilizaria, pois eles usam seu próprio código para gerenciar memória. Outro motivo 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.
fonte
malloc
efree
, interromperá o meu programa correto.free
até terminar. Mas o seu coletor de lixo proposto que não libera a memória até que eu chame explicitamentefree
não é um coletor de lixo.C foi projetado em uma época em que a coleta de lixo era apenas uma opção. Ele também foi projetado para usos em que a coleta de lixo geralmente não funcionaria - bare metal, ambientes em tempo real com memória mínima e suporte a tempo de execução mínimo. Lembre-se de que C era a linguagem de implementação do primeiro unix, executado em um pdp-11 com 64 * K * bytes de memória. O C ++ era originalmente uma extensão do 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 precisa ser construída no térreo.
fonte
Não tenho as aspas exatas, mas Bjarne e Herb Sutter dizem algo ao longo das linhas:
No C ++ moderno, você usa ponteiros inteligentes e, portanto, não tem lixo.
fonte
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 o código que usa os diferentes modelos. Ou seja, se eu escrever um código que suponha que você esteja usando um coletor de lixo, você não poderá usá-lo em seu programa com a coleta de lixo desativada. Se você o fizer, vazará por toda parte.
fonte
Você pode imaginar escrever um manipulador de dispositivo em um idioma com coleta de lixo? Quantos bits poderiam aparecer na linha enquanto o GC estava funcionando?
Ou um sistema operacional? Como você pode 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 agradável que também é uma boa escolha para muitas tarefas de nível superior. Os czares de idiomas estão cientes desses usos, mas precisam oferecer suporte aos requisitos de drivers de dispositivo, código incorporado e sistemas operacionais como prioridade.
fonte
A resposta curta e chata para essa pergunta é que precisa haver uma linguagem que não seja coletada no lixo 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 em execução.
A outra pergunta é por que C e C ++ não possuem coletores de lixo. Bem, eu sei que o C ++ tem alguns deles por aí, mas eles não são muito populares porque são forçados a lidar com uma linguagem que não foi projetada para ser editada por GC em primeiro lugar, e as pessoas que ainda usam o C ++ em essa idade não é realmente do tipo que sente falta de um GC.
Além disso, em vez de adicionar o GC a um idioma antigo que não seja do tipo GC, é realmente mais fácil criar um novo idioma que tenha a mesma sintaxe ao oferecer suporte a um GC. Java e C # são bons exemplos disso.
fonte
Há vários problemas, incluindo ...
delete
oufree
explicitamente. A abordagem do GC ainda tem uma vantagem - sem referências pendentes - e a análise estática pode capturar alguns casos, mas, novamente, não há uma solução perfeita para todos os casos.Basicamente, em parte, é sobre a idade dos idiomas, mas sempre haverá um lugar para idiomas que não são da GC - mesmo que seja um pouco agradável. E sério, em C ++, a falta de GC não é grande coisa - sua memória é gerenciada de maneira diferente, mas não é gerenciada.
O C ++ gerenciado pela Microsofts tem pelo menos alguma capacidade de misturar GC e não GC no mesmo aplicativo, permitindo combinar e combinar as vantagens de cada um, mas não tenho experiência para dizer o quão bem isso funciona na prática.
Links de rep-whoring para respostas relacionadas ...
fonte
A coleta de lixo é fundamentalmente incompatível com uma linguagem de sistemas usada para desenvolver drivers para hardware compatível com DMA.
É perfeitamente possível que o único ponteiro para um objeto seja armazenado em um registro de hardware em algum periférico. Como o coletor de lixo não saberia disso, pensaria que o objeto estava inacessível e o coletaria.
Esse argumento é duplo para compactar o GC. Mesmo se você fosse cuidadoso em manter na memória referências a 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 DMA imóveis e objetos gerenciados por GC, o que significa que você tem todas as desvantagens de ambos.
fonte
Como o C & C ++ é uma linguagem de nível relativamente baixo destinada a fins gerais, pode ser executada, por exemplo, em um processador de 16 bits com 1 MB de memória em um sistema incorporado, que não pode permitir desperdiçar memória com o gc.
fonte
Existem coletores de lixo em C ++ e C. Não tenho certeza de como isso funciona em C, mas em C ++ você pode aproveitar o RTTI para descobrir dinamicamente seu gráfico de objetos e usá-lo para a coleta de lixo.
Que eu saiba, você não pode escrever Java sem um coletor de lixo. Uma pequena pesquisa revelou isso .
A principal diferença entre Java e C / C ++ é que, em C / C ++, a escolha é sempre sua, enquanto em Java você geralmente fica sem opções por design.
fonte
É uma troca entre desempenho e segurança.
Não há garantia de que seu lixo será coletado em Java; portanto, ele pode demorar muito tempo, enquanto a verificação de objetos não referenciados (por exemplo, lixo) também leva mais tempo do que excluir ou liberar explicitamente um objeto não utilizado.
A vantagem é, é claro, que se pode construir uma linguagem sem ponteiros ou sem vazamento de memória, portanto é mais provável que se produza o código correto.
Às vezes, pode haver uma ligeira vantagem "religiosa" nesses debates - seja avisado!
fonte
Aqui está uma lista de problemas inerentes ao GC, que o tornam inutilizável em uma linguagem de sistema como C:
O GC deve executar abaixo do nível do código cujos objetos ele gerencia. Simplesmente não existe esse nível em um kernel.
Um GC deve interromper o código gerenciado de tempos em tempos. Agora pense no que aconteceria se fizesse isso no seu kernel. Todo o processamento em sua máquina para por, digamos, um milissegundo, enquanto o GC verifica todas as alocações de memória existentes. Isso acabaria com 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. Ou seja, ele deve ser capaz de examinar todos os objetos de memória existentes e produzir uma lista de compensações onde seus ponteiros podem ser encontrados.
Essa descoberta deve ser perfeita: o GC deve ser capaz de perseguir todos os indicadores que descobrir. Se desferisse um falso positivo, provavelmente falharia. Se não conseguisse descobrir um falso negativo, provavelmente destruiria um objeto ainda em uso, travando o código gerenciado ou corrompendo silenciosamente seus dados.
Isso exige absolutamente que as informações de tipo sejam armazenadas em todos os objetos existentes. No entanto, C e C ++ permitem objetos de dados antigos 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 ter ordens de magnitude mais rápidas quando não são implementados em Java. E um dos fatores que tornam o Java lento é o GC. É isso que impede que linguagens GCed como Java sejam usadas na supercomputação. Se 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 linguagens criadas para suportar todos os casos de uso possíveis. E, como você vê, 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.
fonte