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

57

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.

Templários das Trevas
fonte
9
Temos um módulo em desenvolvimento para iPhone neste semestre. Depois de codificar aplicativos para Android por 2 anos, essa pergunta atingiu bastante a classe. Somente agora vemos quantas horas o Java realmente nos salvou em não ter que rastrear erros desagradáveis ​​de gerenciamento de memória e não ter que escrever o código da placa da caldeira.
S11
7
@NullUserException, pois não especifica uma maneira de recuperar a memória que praticamente implica em um GC.
Winston Ewert
11
@ bizso09: Você já viu o ARC? Não há necessidade de lento / gordura GC / não-determinístico quando você tem sistema de suporte para contagem de referência: developer.apple.com/technologies/ios5
JBRWilkinson
3
As respostas para essa boa pergunta estão cheias de besteira religiosa.
precisa saber é o seguinte
11
Em C e C ++, é possível pegar um ponteiro, convertê-lo em int e adicionar um número a ele. Posteriormente subtraia o número do int e converta o resultado de volta para um ponteiro. Você receberá o mesmo ponteiro de antes. Boa sorte na implementação de um GC que NÃO coleta essa memória enquanto seu endereço é armazenado apenas na variável que também possui outro valor. Eu sei que o exemplo é bobo, mas uma lista vinculada ao XOR usa algo semelhante. Eu postaria isso como uma resposta, mas a pergunta está encerrada.
Marian Spanik

Respostas:

71

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 ++.

kylben
fonte
26
Uma questão secundária é que o GC não é determinístico. O objeto pode ou não estar na memória muito tempo depois que o programa o "soltou". Os ciclos de vida da recontagem são determinísticos; quando a última referência é descartada, a memória é descartada. Isso tem implicações não apenas na eficiência da memória, mas também na depuração. Um erro de programação comum é o objeto "zumbi", memória de referência que teoricamente foi descartada. É muito mais provável que o GC mascara esse efeito e produza bugs intermitentes e extremamente difíceis de rastrear.
Kllben #
22
- o GC moderno não rastreia alocações ou conta referências. Eles constroem um gráfico a partir de tudo o que está atualmente na pilha e apenas condensam e limpam todo o resto (simplificado), e o GC normalmente resulta em menor complexidade da linguagem. Até o benefício de desempenho é questionável.
Joel Coehoorn
13
Er, @kylben, o objetivo de ter o GC automático incorporado na linguagem é que fica impossível fazer referência a objetos zumbis, porque o GC libera apenas objetos impossíveis de fazer referência! Você obtém os tipos de erros difíceis de rastrear de que fala quando comete erros no gerenciamento manual de memória.
Ben
14
-1, o GC não conta referências. Além disso, dependendo do seu uso de memória e esquema de alocação, um GC pode ser mais rápido (com uma sobrecarga no uso de memória). Portanto, o argumento sobre desempenho também é falácia. Somente a proximidade do metal é realmente um ponto válido.
Deadalnix 9/10/11
14
Nem Java nem C # usam contagem de referência: os esquemas de GC baseados na contagem de referência são bastante primitivos em comparação e têm desempenho muito pior que os coletores de lixo modernos (principalmente porque eles precisam fazer gravações na memória para alterar as contagens de referência sempre que você copiar uma referência!)
mikera
44

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.

Charles E. Grant
fonte
2
+1 um para a boa resposta geral, mas também especialmente para "O preço pago é que a linguagem C fornece muito pouca ajuda na escrita de código livre correta, bug"
Shivan Dragão
2
C tem gerenciamento de memória - mas simplesmente funciona, então as pessoas quase não percebem. Há memória estática, registradores e a pilha. Até você começar a alocar fora da pilha, você está bem e elegante. É a alocação de pilha que atrapalha as coisas. Quanto ao Java, todos podem escrever seu próprio tempo de execução Java - há muito por onde escolher, incluindo o que poderia ser chamado de "Java do sistema". O .NET pode fazer praticamente tudo o que C pode - apenas fica atrás dos recursos nativos do C ++ (por exemplo, as classes são gerenciadas apenas no .NET). Obviamente, você também possui o C ++. NET, que tem tudo o que o C ++ faz e tudo o que o .NET faz.
Luaan
11
@Luaan Eu diria que é uma definição muito generosa de ter "gerenciamento de memória" "Até você começar a alocar fora da pilha, você está bem e elegante. É a alocação da pilha que atrapalha as coisas", é como dizer que um carro é um avião perfeitamente bom, simplesmente não é capaz de voar.
Charles E. Grant
11
@ CharlesE.Grant Bem, uma linguagem puramente funcional pode fazer tudo com esse tipo de gerenciamento de memória. Só porque a alocação de heap é uma boa opção em alguns casos de uso, não significa que seja a referência para todos os idiomas / tempos de execução. Não é como se o gerenciamento de memória parasse de ser "gerenciamento de memória" apenas porque é simples, direto e oculto nos bastidores. O design da alocação de memória estática ainda é um gerenciamento de memória, assim como um bom uso da pilha e o que mais você tiver disponível.
Luaan 17/06/16
"qualquer implementação compatível com padrão" não é verdadeira, apenas para implementação em ambiente host compatível com padrão. Algumas plataformas / bibliotecas padrão, a maioria para microcontroladores embarcados de 8 ou 16 bits, não fornecem malloc()ou free(). (por exemplo, compiladores MLAP para PIC)
12431234123412341234123
32

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 usingpalavra-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.

Daniel Pryden
fonte
4
Esta é a resposta real para a questão
coredump
11
Para a parte c ++, hoje em dia você deve olhar para std :: unique_ptr e std :: move :) #
Niclas Larsson #
@NiclasLarsson: Não sei se entendi o seu ponto. Você está dizendo que 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 consideraria std:unique_ptrum 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 (e std::moveé o mecanismo de atualização da contagem de referências).
Daniel Pryden 31/03
std::unique_ptrnão possui uma contagem de referência e std::movenão tem nada a ver com referências (portanto, não há desempenho atingido). Eu vejo o seu ponto, porém, como std::shared_ptrtem uma contagem de referência que é implicitamente atualizado por std::move:)
Niclas Larsson
2
@ Mike76: No lado da alocação, um alocador de GC trabalhará tão rápido quanto a alocação de pilha, e o GC pode desalocar milhares de objetos ao mesmo tempo. Não importa o que você faça com uma implementação de ref-counting, a alocação e desalocação nunca serão mais rápidas que malloce free. 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)
Daniel Pryden
27

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 ++.

Lior Kogan
fonte
Interessante. Meu material de leitura para hoje.
surfasb
Bah, um vídeo. Mas nunca menos, interessante já.
surfasb
2
vídeo interessante. 21 minutos e 55 minutos foram os melhores momentos. Pena que as chamadas do WinRT ainda pareciam ser C ++ / CLI bumpf.
Gbjbaanb
2
@ dan04: Isso é verdade. Mas então, se você escreve em C, recebe o que pede.
DeadMG
6
O gerenciamento dos ponteiros inteligentes não é mais exigente do que garantir que você não tenha referências desnecessárias em um ambiente de coleta de lixo. Como o GC não consegue ler sua mente, também não é mágico.
Tamás Szelei 14/10/11
15

"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.

ChrisF
fonte
11
Mas os programas futuros escritos começariam a usar o coletor de lixo, não?
Escuro Templar
5
Embora a coleta de lixo seja teoricamente independente de qualquer linguagem de programação, é muito difícil escrever um GC útil para C / C ++ e até mesmo impossível torná-lo à prova de idiotas (pelo menos tão à prova de idiotas quanto as de Java) - a razão pela qual o Java pode obtê-lo off é porque é executado em um ambiente virtualizado controlado. Por outro lado, a linguagem Java foi projetada para GC, e você terá dificuldade em escrever um compilador Java que não faça GC.
tdammers
4
@ Tdammers: Eu concordo que a coleta de lixo precisa ser suportada pelo idioma para que seja possível. No entanto, o ponto principal não é a virtualização e o ambiente controlado, mas a digitação estrita. C e C ++ são fracamente tipados, portanto, permitem coisas como armazenar ponteiros em variáveis ​​inteiras, reconstruir ponteiros de deslocamentos e impedir que o coletor seja capaz de dizer com segurança o que é acessível (o C ++ 11 proíbe posteriormente permitir pelo menos colecionadores conservadores). Em Java, você sempre sabe o que é uma referência, para poder coletá-la com precisão, mesmo se compilada como nativa.
Jan Hudec 10/10
2
@ ThorbjørnRavnAndersen: Eu posso escrever um programa C válido que armazena ponteiros de forma que nenhum coletor de lixo possa encontrá-los. Se você conectar um coletor de lixo ao malloce free, interromperá o meu programa correto.
Ben Voigt
2
@ ThorbjørnRavnAndersen: Não, eu não ligaria freeaté terminar. Mas o seu coletor de lixo proposto que não libera a memória até que eu chame explicitamente freenão é um coletor de lixo.
Ben Voigt
12

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.

ddyer
fonte
9

Não tenho as aspas exatas, mas Bjarne e Herb Sutter dizem algo ao longo das linhas:

O C ++ não precisa de um coletor de lixo, porque não possui lixo.

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

ronag
fonte
11
o que são indicadores inteligentes?
Escuro Templar
11
se fosse assim tão simples, ninguém teria implementado nenhum GC.
Deadalnix 9/10/11
7
@deadalnix: Certo, porque ninguém nunca implementa algo excessivamente complicado, lento, inchado ou desnecessário. Todo o software é 100% eficiente o tempo todo, certo?
Zach
5
@deadalnix - A abordagem C ++ para gerenciamento de memória é mais recente que os coletores de lixo. RAII foi inventado por Bjarne Stroustrup para C ++. A limpeza do destruidor é uma ideia antiga, mas as regras para garantir a segurança da exceção são fundamentais. Não sei exatamente quando a idéia foi descrita pela primeira vez, mas o primeiro padrão C ++ foi finalizado em 1998, e o Stroustrups "Design e Evolução do C ++" não foi publicado até 1994, e as exceções foram uma adição relativamente recente ao C ++ - após a publicação do "Annotated C ++ Reference Manual" em 1990, acredito. GC foi inventado em 1959 para Lisp.
Steve314
11
@deadalnix - você está ciente de que pelo menos uma Java VM usou um GC de contagem de referência que poderia (quase) ser implementado usando o RAII estilo C ++ usando uma classe de ponteiro inteligente - precisamente porque era mais eficiente para código multithread do que as VMs existentes? Consulte www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf. Um motivo pelo qual você não vê isso no C ++ na prática é a coleta usual de GC - ele pode coletar ciclos, mas não pode escolher uma ordem segura de destruidor na presença de ciclos e, portanto, não pode garantir uma limpeza confiável do destruidor.
Steve314
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 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.

Winston Ewert
fonte
6

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.

James Anderson
fonte
2
C bom para alto nível? Eu bufei minha bebida por todo o teclado.
23911 DeadMG
5
Bem, ele disse "muitas tarefas de nível superior". Ele poderia estar contando trolls (um, dois, muitos ...). E ele realmente não disse mais do que o que. Brincadeiras à parte, embora, é verdade - a evidência é que muitos projetos significativos de nível superior têm sido desenvolvidos com sucesso em C. Pode haver melhores opções agora para muitos desses projetos, mas um projeto de trabalho é a evidência mais forte do que a especulação sobre o que poderia têm estado.
Steve314
Existem alguns sistemas operacionais gerenciados e eles funcionam muito bem. De fato, quando você gerencia todo o sistema, o desempenho atingido pelo uso de código gerenciado cai ainda mais, até ser mais rápido que o código não gerenciado em cenários da vida real. Obviamente, esses são todos "sistemas operacionais de pesquisa" - não há praticamente nenhuma maneira de torná-los compatíveis com o código não gerenciado existente, além de criar um sistema operacional não gerenciado totalmente virtualizado no sistema operacional gerenciado. A Microsoft sugeriu em algum momento que eles poderiam substituir o Windows Server por um deles, pois cada vez mais códigos de servidor são gravados no .NET.
Luaan
6

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.

hugomg
fonte
11
Em algum lugar no programmers.se ou no SO, há uma alegação de que alguém me disse que alguém estava trabalhando em uma coisa coletada por lixo com auto-inicialização - IIRC basicamente implementando a VM usando uma linguagem GC, com um subconjunto de inicialização usado para implementar o próprio GC. Eu esqueço o nome Quando olhei para ele, descobriu-se que eles basicamente nunca haviam alcançado o salto do subconjunto sem GC para o nível de trabalho GC. Isso é possível em princípio, mas o AFAIK nunca foi alcançado na prática - é certamente um caso de fazer as coisas da maneira mais difícil.
Steve314
@ Steve314: Eu adoraria ver isso se você se lembrar de onde a encontrou!
hugomg 15/10
encontrei! Consulte os comentários em stackoverflow.com/questions/3317329/… referentes à VM Klein. Parte do problema em encontrá-lo - a questão foi encerrada.
Steve314
BTW - Parece que não consigo iniciar meus comentários com @missingno - o que dá?
Steve314
@ steve314: Após escrever a resposta à qual este tópico está associado, já recebo uma notificação por todos os comentários. Fazer um @ -post nesse caso seria redundante e não é permitido pelo SE (não me pergunte por que). (A causa real, porém, é porque o meu número está faltando)
hugomg
5

Há 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 plataforma de GC sem uma linguagem não-GC subjacente.
  • Embora o GC seja comprovadamente mais eficiente do que o não GC para códigos de aplicativos típicos desenvolvidos em prazos típicos, etc, há problemas em que mais esforço de desenvolvimento é uma boa opção e o gerenciamento de memória especializado supera um GC de uso geral. Além disso, o C ++ geralmente é comprovadamente mais eficiente do que a maioria das linguagens de GC, mesmo sem nenhum esforço extra de desenvolvimento.
  • O GC não é universalmente mais seguro que o RAII estilo C ++. O RAII permite que recursos diferentes da memória sejam limpos automaticamente, basicamente porque suporta destruidores confiáveis ​​e oportunos. Eles não podem ser combinados com os métodos convencionais de GC devido a problemas nos ciclos de referência.
  • Os idiomas do 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 deleteou freeexplicitamente. 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 ...

Steve314
fonte
4

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.

Ben Voigt
fonte
Indiscutivelmente todas as desvantagens de ambos, mas menos instâncias de cada desvantagem e o mesmo para vantagens. Claramente, há complexidade em ter mais tipos de gerenciamento de memória, mas também pode ser evitada escolhendo o cavalo certo para cada curso em seu código. Improvável, imagino, mas há uma lacuna teórica lá. Especulei sobre a combinação de GC e não-GC no mesmo idioma antes, mas não para drivers de dispositivo - mais por ter um aplicativo principalmente de GC, mas com algumas bibliotecas de estrutura de dados de baixo nível gerenciadas por memória manualmente.
Steve314
@ Steve314: Você não diria que lembrar de quais objetos precisam ser liberados manualmente é tão oneroso quanto lembrar de liberar tudo? (Obviamente, ponteiros inteligentes podem ajudar com qualquer um deles, portanto nenhum deles é um grande problema). E você precisa de conjuntos diferentes para objetos gerenciados manualmente versus objetos coletados / compactáveis, pois a compactação não funciona bem quando há objetos fixos espalhados. Portanto, muita complexidade extra por nada.
Ben Voigt
2
Não, se houver uma clara divisão entre o código de alto nível, que é todo o GC, e o código de baixo nível, que opta pelo GC. Eu desenvolvi a idéia principalmente ao olhar para D há alguns anos, o que permite que você opte por não participar do GC, mas não permite que você volte a participar. Tomemos, por exemplo, uma biblioteca em árvore B +. O contêiner como um todo deve ser o GC, mas os nós da estrutura de dados provavelmente não o são - é mais eficiente fazer uma varredura personalizada apenas nos nós folha do que fazer com que o GC faça uma pesquisa recursiva nos nós de ramificação. No entanto, essa verificação precisa relatar os itens contidos para o GC.
Steve314
O ponto é que essa é uma parte da funcionalidade contida. Tratar os nós da árvore B + como gerenciamento de memória WRT especial não é diferente de tratá-los como WRT especial, sendo nós da árvore B +. É uma biblioteca encapsulada e o código do aplicativo não precisa saber que o comportamento do GC foi ignorado / diferenciado. Exceto que, pelo menos na época, isso era impossível em D - como eu disse, não havia como voltar e relatar os itens contidos ao GC como possíveis raízes do GC.
Steve314
3

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.

Petruza
fonte
11
"Sistema embarcado"? No momento em que C foi padronizado (1989), era necessário poder lidar com PCs com 1 MB de memória.
Dan04 17/10/11
Concordo, estava citando um exemplo mais atual.
Petruza 17/10/11
1MB ??? Puta merda, quem precisaria de tanta memória RAM? </billGates>
Mark K Cowan
2

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.

back2dos
fonte
E também que os coletores de lixo dedicados sejam melhor implementados, mais eficientes e se encaixem melhor no idioma. :)
Max
Não, você não pode usar o RTTI para descobrir dinamicamente o gráfico de objetos em C / C ++: são os objetos de dados antigos simples que estragam tudo. Simplesmente, não há informações de RTTI armazenadas em um objeto de dados antigo simples que permitisse a um coletor de lixo diferenciar entre ponteiros e não ponteiros dentro desse objeto. Pior ainda, os ponteiros não precisam estar perfeitamente alinhados em todo o hardware; portanto, dado um objeto de 16 bytes, existem 9 locais possíveis para armazenar um ponteiro de 64 bits, apenas dois deles não se sobrepõem.
C6 /
2

É 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!

adrianmcmenamin
fonte
1

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.

cmaster
fonte