Não estou fazendo essa pergunta por causa dos méritos da coleta de lixo antes de tudo. Minha principal razão para perguntar isso é que eu sei que Bjarne Stroustrup disse que o C ++ terá um coletor de lixo em algum momento.
Com isso dito, por que não foi adicionado? Já existem alguns coletores de lixo para C ++. Essa é apenas uma daquelas coisas do tipo "mais fácil dizer do que fazer"? Ou há outros motivos para não ter sido adicionado (e não será adicionado no C ++ 11)?
Links cruzados:
Apenas para esclarecer, entendo os motivos pelos quais o C ++ não tinha um coletor de lixo quando foi criado. Gostaria de saber por que o colecionador não pode ser adicionado.
c++
garbage-collection
c++11
Jason Baker
fonte
fonte
Respostas:
A coleta de lixo implícita poderia ter sido adicionada, mas simplesmente não foi suficiente. Provavelmente devido não apenas a complicações de implementação, mas também porque as pessoas não conseguem chegar a um consenso geral com rapidez suficiente.
Uma citação do próprio Bjarne Stroustrup:
Há uma boa discussão sobre o tópico aqui .
Visao geral:
O C ++ é muito poderoso e permite que você faça quase tudo. Por esse motivo, não impõe automaticamente muitas coisas que podem afetar o desempenho. A coleta de lixo pode ser facilmente implementada com ponteiros inteligentes (objetos que agrupam ponteiros com uma contagem de referência, que são excluídos automaticamente quando a contagem de referência atinge 0).
O C ++ foi criado com os concorrentes em mente que não tinham coleta de lixo. A eficiência foi a principal preocupação da qual o C ++ teve que afastar as críticas em comparação com o C e outros.
Existem 2 tipos de coleta de lixo ...
Coleta de lixo explícita:
C ++ 0x terá coleta de lixo por meio de ponteiros criados com shared_ptr
Se você quiser, poderá usá-lo; se não quiser, não será obrigado a usá-lo.
Atualmente, você também pode usar o boost: shared_ptr, se não quiser esperar pelo C ++ 0x.
Coleta de lixo implícita:
Porém, ele não possui coleta de lixo transparente. Porém, será um ponto de foco para futuras especificações do C ++.
Por que o Tr1 não possui coleta de lixo implícita?
Há muitas coisas que o tr1 do C ++ 0x deveria ter tido, Bjarne Stroustrup em entrevistas anteriores afirmou que o tr1 não tinha tanto quanto ele gostaria.
fonte
smart_ptr's
? Como você faria bifurcação de baixo nível no estilo Unix, com um coletor de lixo no caminho? Outras coisas seriam afetadas, como rosqueamento. O Python possui seu bloqueio global de intérpretes principalmente por causa de sua coleta de lixo (consulte Cython). Mantenha-o fora do C / C ++, obrigado.std::shared_ptr
) são as referências cíclicas, que causam vazamento de memória. Portanto, você deve usarstd::weak_ptr
com cuidado para interromper os ciclos, o que é confuso. Marcar e varrer o estilo GC não tem esse problema. Não há incompatibilidade inerente entre threading / bifurcação e coleta de lixo. Java e C # têm multithreading preemptivo de alto desempenho e um coletor de lixo. Há problemas relacionados a aplicativos em tempo real e a um coletor de lixo, pois a maioria dos coletores de lixo precisa parar o mundo para rodar.std::shared_ptr
) são as referências cíclicas" e o desempenho péssimo, o que é irônico, porque o melhor desempenho geralmente é a justificativa para o uso de C ++ ... flyingfrogblog.blogspot.co.uk/2011/01/…Para adicionar ao debate aqui.
Existem problemas conhecidos com a coleta de lixo, e entendê-los ajuda a entender por que não há nenhum em C ++.
1. desempenho?
A primeira reclamação geralmente é sobre desempenho, mas a maioria das pessoas não percebe do que está falando. Conforme ilustrado pelo
Martin Beckett
problema, pode não ser o desempenho em si, mas a previsibilidade do desempenho.Atualmente, existem duas famílias de GC amplamente implantadas:
A
Mark And Sweep
é mais rápido (menos impacto no desempenho geral), mas sofre de uma "congelar o mundo" síndrome: ou seja, quando os GC entra em ação, tudo o resto está parado até que o GC tem feito a sua limpeza. Se você deseja construir um servidor que responda em alguns milissegundos ... algumas transações não corresponderão às suas expectativas :)O problema de
Reference Counting
é diferente: a contagem de referência aumenta a sobrecarga, especialmente em ambientes com vários segmentos, porque você precisa ter uma contagem atômica. Além disso, há o problema dos ciclos de referência, portanto, você precisa de um algoritmo inteligente para detectar e eliminá-los (geralmente implementado por um "congelamento do mundo" também, embora seja menos frequente). Em geral, a partir de hoje, esse tipo (embora normalmente seja mais responsivo ou melhor, congela com menos frequência) é mais lento que oMark And Sweep
.Eu vi um artigo de implementadores da Eiffel que estavam tentando implementar um
Reference Counting
Garbage Collector que teria um desempenho global semelhanteMark And Sweep
sem o aspecto "Congelar o mundo". Exigia um thread separado para o GC (típico). O algoritmo era um pouco assustador (no final), mas o artigo fez um bom trabalho ao introduzir os conceitos um de cada vez e mostrar a evolução do algoritmo da versão "simples" para a versão completa. Leitura recomendada se eu pudesse colocar minhas mãos de volta no arquivo PDF ...2. Aquisição de recursos é inicialização (RAII)
É um idioma comum,
C++
pois você agrupará a propriedade dos recursos em um objeto para garantir que eles sejam liberados corretamente. É usado principalmente para memória, pois não temos coleta de lixo, mas também é útil para muitas outras situações:A idéia é controlar adequadamente a vida útil do objeto:
O problema do GC é que, se ele ajuda com o primeiro e garante que mais tarde ... esse "final" pode não ser suficiente. Se você liberar um bloqueio, realmente gostaria que ele fosse liberado agora, para que não bloqueie mais chamadas!
Os idiomas com GC têm duas soluções alternativas:
using
construir ... mas é RAII explícito (fraco) enquanto estiver em C ++ RAII está implícito, de modo que o usuário NÃO PODE involuntariamente cometer o erro (omitindo ausing
palavra - chave)3. Ponteiros inteligentes
Ponteiros inteligentes geralmente aparecem como uma bala de prata para lidar com a memória
C++
. Muitas vezes ouvi falar: afinal, não precisamos de GC, pois temos indicadores inteligentes.Não se poderia estar mais errado.
Ponteiros inteligentes ajudam:
auto_ptr
eunique_ptr
usam conceitos RAII, extremamente úteis. Eles são tão simples que você pode escrevê-los sozinho com bastante facilidade.Quando é necessário compartilhar a propriedade, porém, fica mais difícil: você pode compartilhar entre vários threads e existem alguns problemas sutis no manuseio da contagem. Portanto, alguém naturalmente vai em direção
shared_ptr
.É ótimo, é para isso que serve o Boost, afinal, mas não é uma bala de prata. Na verdade, o principal problema
shared_ptr
é que ele emula um GC implementado por,Reference Counting
mas você precisa implementar a detecção de ciclo sozinho.É claro que existe isso
weak_ptr
, mas infelizmente eu já vi vazamentos de memória, apesar do usoshared_ptr
devido a esses ciclos ... e quando você está em um ambiente com vários threads, é extremamente difícil de detectar!4. Qual é a solução?
Não existe uma bala de prata, mas como sempre, é definitivamente viável. Na ausência de GC, é preciso ser claro quanto à propriedade:
weak_ptr
Portanto, seria ótimo ter um GC ... no entanto, não é um problema trivial. E nesse meio tempo, só precisamos arregaçar as mangas.
fonte
Que tipo? deve ser otimizado para controladores de máquinas de lavar, telefones celulares, estações de trabalho ou supercomputadores?
Deve priorizar a capacidade de resposta da GUI ou o carregamento do servidor?
deveria usar muita memória ou muita CPU?
C / c ++ é usado em muitas circunstâncias diferentes. Eu suspeito que algo como impulsionar ponteiros inteligentes será suficiente para a maioria dos usuários
Editar - Os coletores de lixo automáticos não são um problema de desempenho (você sempre pode comprar mais servidores); é uma questão de desempenho previsível.
Não saber quando o GC vai entrar é como empregar um piloto de avião narcoléptico, na maioria das vezes eles são ótimos - mas quando você realmente precisa de capacidade de resposta!
fonte
Uma das maiores razões pelas quais o C ++ não incorporou a coleta de lixo é que fazer com que a coleta seja agradável com os destruidores é muito, muito difícil. Até onde eu sei, ninguém realmente sabe como resolvê-lo completamente ainda. Existem muitos problemas para lidar com:
Estes são apenas alguns dos problemas enfrentados.
fonte
Dispose
um objeto pode torná-lo impossível, mas as referências que apontaram para o objeto quando ele estava vivo continuarão a fazê-lo depois que ele estiver morto. Por outro lado, em sistemas que não são da GC, os objetos podem ser excluídos enquanto as referências existem, e raramente existe um limite para o caos que pode ser causado se uma dessas referências for usada.Embora essa seja uma pergunta antiga , ainda há um problema que não vejo ninguém abordando: a coleta de lixo é quase impossível de especificar.
Em particular, o padrão C ++ é bastante cuidadoso ao especificar a linguagem em termos de comportamento observável externamente, em vez de como a implementação alcança esse comportamento. No caso da coleta de lixo, no entanto, não é virtualmente nenhum externamente comportamento observável.
A idéia geral da coleta de lixo é que ela faça uma tentativa razoável de garantir que uma alocação de memória seja bem-sucedida. Infelizmente, é essencialmente impossível garantir que qualquer alocação de memória seja bem-sucedida, mesmo se você tiver um coletor de lixo em operação. Isso é verdade até certo ponto em qualquer caso, mas particularmente no caso do C ++, porque (provavelmente) não é possível usar um coletor de cópias (ou qualquer coisa semelhante) que mova objetos na memória durante um ciclo de coleta.
Se você não pode mover objetos, não pode criar um espaço de memória único e contíguo do qual fazer suas alocações - e isso significa que sua pilha (ou armazenamento gratuito ou o que você preferir chamar) pode e provavelmente irá , tornam-se fragmentados ao longo do tempo. Isso, por sua vez, pode impedir que uma alocação seja bem-sucedida, mesmo quando houver mais memória livre do que a quantidade solicitada.
Embora seja possível obter alguma garantia que diga (em essência) que, se você repetir exatamente o mesmo padrão de alocação repetidamente e tiver sido bem-sucedido na primeira vez, continuará tendo êxito nas iterações subsequentes, desde que a memória alocada tornou-se inacessível entre iterações. Essa é uma garantia tão fraca que é essencialmente inútil, mas não vejo nenhuma esperança razoável de fortalecê-la.
Mesmo assim, é mais forte do que o proposto para C ++. A proposta anterior [aviso: PDF] (que foi descartado) não garante nada. Em 28 páginas da proposta, o que você colocou no caminho do comportamento observável externamente foi uma nota única (não normativa) dizendo:
Pelo menos para mim, isso levanta uma questão séria sobre o retorno do investimento. Vamos quebrar o código existente (ninguém sabe exatamente quanto, mas definitivamente um pouco), colocar novos requisitos em implementações e novas restrições no código, e o que recebemos em troca é possivelmente nada?
Mesmo na melhor das hipóteses, o que obtemos são programas que, com base em testes com Java , provavelmente exigirão cerca de seis vezes mais memória para rodar na mesma velocidade que agora. Pior ainda, a coleta de lixo fazia parte do Java desde o início - o C ++ impõe restrições suficientes ao coletor de lixo, o que quase certamente terá uma relação custo / benefício ainda pior (mesmo se formos além do que a proposta garantia e supomos que haveria) algum benefício).
Eu resumiria a situação matematicamente: essa é uma situação complexa. Como qualquer matemático sabe, um número complexo tem duas partes: real e imaginário. Parece-me que o que temos aqui são custos reais, mas benefícios que são (pelo menos principalmente) imaginários.
fonte
free
para você (onde eu quero dizerfree
análogo ao idioma C). Mas o Java nunca garante chamar finalizadores ou algo assim. De fato, o C ++ faz muito mais que o Java para executar gravações de banco de dados de confirmação, liberando identificadores de arquivos e assim por diante. Java alega ter "GC", mas os desenvolvedores de Java precisam ligar meticulosamenteclose()
o tempo todo e precisam estar muito conscientes do gerenciamento de recursos, tomando cuidado para não ligarclose()
muito cedo ou muito tarde. C ++ nos liberta disso. ... (continuação)try (Whatever w=...) {...}
resolve (e você recebe um aviso quando se esquece). Os restantes também são problemáticos com o RAII. Chamarclose()
"o tempo todo" significa talvez uma vez por dezenas de mil linhas, portanto não é tão ruim assim, enquanto a memória é alocada quase em todas as linhas Java.Fonte: http://www.stroustrup.com/bs_faq.html#garbage-collection
Quanto ao porquê de ele não tê-lo construído em, Se bem me lembro que foi inventado antes de GC foi a coisa , e eu não acredito que a linguagem poderia ter tido GC por várias razões (compatibilidade IE para trás com C)
Espero que isto ajude.
fonte
Stroustrup fez alguns bons comentários sobre isso na conferência Going Native de 2013.
Basta pular para cerca de 25m50s neste vídeo . (Eu recomendo assistir o vídeo inteiro, na verdade, mas isso pula para as coisas sobre coleta de lixo.)
Quando você tem uma linguagem realmente boa que facilita (e é seguro, previsível, fácil de ler e fácil de ensinar) lidar com objetos e valores de maneira direta, evitando o uso (explícito) do pilha, então você nem quer coleta de lixo.
Com o C ++ moderno e o material que temos no C ++ 11, a coleta de lixo não é mais desejável, exceto em circunstâncias limitadas. De fato, mesmo que um bom coletor de lixo seja incorporado a um dos principais compiladores de C ++, acho que ele não será usado com muita frequência. Será mais fácil , e não mais difícil, evitar o GC.
Ele mostra este exemplo:
Isso não é seguro em C ++. Mas também não é seguro em Java! No C ++, se a função retornar mais cedo,
delete
nunca será chamada. Mas se você teve uma coleta de lixo completa, como em Java, você apenas recebe uma sugestão de que o objeto será destruído "em algum momento no futuro" ( atualização: é ainda pior que isso. Java nãoprometa ligar para o finalizador de todos os tempos - talvez nunca seja chamado). Isso não é suficiente se o Gadget tiver um identificador de arquivo aberto, uma conexão com um banco de dados ou dados que você armazenou em buffer para gravação em um banco de dados posteriormente. Queremos que o Gadget seja destruído assim que terminar, para liberar esses recursos o mais rápido possível. Você não quer que seu servidor de banco de dados esteja enfrentando milhares de conexões de banco de dados que não são mais necessárias - ele não sabe que seu programa terminou de funcionar.Então, qual é a solução? Existem algumas abordagens. A abordagem óbvia, que você usará para a grande maioria dos seus objetos é:
Isso leva menos caracteres para digitar. Não está
new
atrapalhando. Não requer que você digiteGadget
duas vezes. O objeto é destruído no final da função. Se é isso que você deseja, isso é muito intuitivo.Gadget
s se comportam da mesma forma queint
oudouble
. Previsível, fácil de ler e fácil de ensinar. Tudo é um "valor". Às vezes, um grande valor, mas os valores são mais fáceis de ensinar, porque você não tem essa coisa de "ação à distância" que obtém com ponteiros (ou referências).A maioria dos objetos que você cria destina-se a ser usada apenas na função que os criou e, talvez, passada como entrada para funções filho. O programador não deveria ter que pensar em 'gerenciamento de memória' ao retornar objetos ou compartilhar objetos em partes amplamente separadas do software.
O escopo e a vida útil são importantes. Na maioria das vezes, é mais fácil se a vida útil for igual à do escopo. É mais fácil de entender e mais fácil de ensinar. Quando você deseja uma vida útil diferente, deve ser óbvio ler o código em que está fazendo isso, usando,
shared_ptr
por exemplo. (Ou retornando objetos (grandes) por valor, aproveitando a semântica de movimento ouunique_ptr
.Isso pode parecer um problema de eficiência. E se eu quiser devolver um gadget
foo()
? A semântica de movimentação do C ++ 11 facilita o retorno de objetos grandes. Basta escreverGadget foo() { ... }
e ele funcionará e funcionará rapidamente. Você não precisa mexer com&&
você mesmo, basta devolver as coisas por valor, e o idioma geralmente poderá fazer as otimizações necessárias. (Mesmo antes do C ++ 03, os compiladores faziam um trabalho notavelmente bom em evitar cópias desnecessárias.)Como Stroustrup disse em outro lugar do vídeo (parafraseando): "Somente um cientista da computação insistiria em copiar um objeto e depois destruir o original. (A platéia ri). Por que não apenas mover o objeto diretamente para o novo local? É isso que os humanos (não cientistas da computação) esperam ".
Quando você pode garantir que apenas uma cópia de um objeto é necessária, é muito mais fácil entender a vida útil do objeto. Você pode escolher qual política de vida útil deseja e a coleta de lixo estará disponível, se desejar. Mas quando você entender os benefícios de outras abordagens, verá que a coleta de lixo está no final da sua lista de preferências.
Se isso não funcionar para você, você pode usar
unique_ptr
ou, na sua faltashared_ptr
,. O C ++ 11 bem escrito é mais curto, mais fácil de ler e mais fácil de ensinar do que muitos outros idiomas quando se trata de gerenciamento de memória.fonte
Gadget
não pedir mais nada para fazer algo em seu nome, o código original seria perfeitamente seguro em Java se adelete
instrução sem sentido (para Java) fosse removida.shared_ptr<T>
especialmente quandoT
é 'chato'. Ele poderia decidir não gerenciar um contador ref para esse tipo e, em vez disso, usar o GC. Isso permitiria que o GC fosse usado sem que o desenvolvedor precisasse notá-lo. Ashared_ptr
poderia simplesmente ser visto como um ponteiro de GC, para adequadoT
. Mas há limitações nisso, e isso tornaria muitos programas mais lentos.string1=string2;
será executada muito rapidamente, independentemente do comprimento da string (literalmente nada mais é do que uma carga e armazenamento de registros) e não requer nenhum bloqueio para garantir que, se a instrução acima for executada enquantostring2
estiver sendo gravado,string1
conterá o valor antigo ou o novo valor, sem comportamento indefinido).shared_ptr<String>
requer muita sincronização nos bastidores, e a atribuição de aString
pode se comportar de maneira estranha se uma variável for lida e gravada simultaneamente. Casos em que alguém gostaria de escrever e lerString
simultaneamente não são muito comuns, mas podem surgir se, por exemplo, algum código desejar disponibilizar relatórios de status em andamento para outros threads. No .NET e Java, essas coisas simplesmente "funcionam".Porque o C ++ moderno não precisa de coleta de lixo.
A resposta das perguntas frequentes de Bjarne Stroustrup sobre esse assunto diz :
A situação, para o código escrito atualmente (C ++ 17 e seguindo as Diretrizes Principais oficiais ), é a seguinte:
"Oh sim? Mas e quanto a ...
... se eu apenas escrever código da maneira que costumávamos escrever C ++ nos velhos tempos? "
Na verdade, você pode simplesmente desconsiderar todas as diretrizes e escrever um código de aplicativo com vazamento - e ele será compilado e executado (e vazado), como sempre.
Mas não é uma situação "simplesmente não faça isso", onde se espera que o desenvolvedor seja virtuoso e exerça muito autocontrole; não é mais simples escrever código não conforme, nem é mais rápido escrever, nem tem melhor desempenho. Gradualmente, também será mais difícil escrever, pois você enfrentaria uma crescente "incompatibilidade de impedância" com o que o código em conformidade fornece e espera.
... se eu
reintrepret_cast
? Ou o ponteiro complexo é aritmético? Ou outros desses hacks? "De fato, se você se dedicar a isso, poderá escrever um código que atrapalhe as coisas, apesar de ser agradável com as diretrizes. Mas:
... desenvolvimento de biblioteca? "
Se você é um desenvolvedor de bibliotecas C ++, escreve um código inseguro envolvendo ponteiros brutos e é obrigado a codificar com cuidado e responsabilidade - mas esses são trechos de código independentes escritos por especialistas (e mais importante, revisados por especialistas).
Então, é exatamente como Bjarne disse: Não há realmente motivação para coletar lixo em geral, pois todos menos se certificam de não produzir lixo. O GC está se tornando um problema com o C ++.
Isso não quer dizer que o GC não seja um problema interessante para certas aplicações específicas, quando você deseja empregar estratégias personalizadas de alocação e desalocação. Para aqueles que você deseja alocação e desalocação personalizadas, não um GC no nível do idioma.
fonte
A idéia por trás do C ++ era que você não pagaria nenhum impacto no desempenho pelos recursos que não usa. Portanto, adicionar coleta de lixo significaria ter alguns programas executados diretamente no hardware, como C e outros em algum tipo de máquina virtual de tempo de execução.
Nada impede que você use algum tipo de ponteiro inteligente vinculado a algum mecanismo de coleta de lixo de terceiros. Eu me lembro da Microsoft fazendo algo parecido com o COM e não deu certo.
fonte
Para responder à maioria das perguntas "por que" sobre C ++, leia Design e evolução do C ++
fonte
Um dos princípios fundamentais por trás da linguagem C original é que a memória é composta por uma sequência de bytes, e o código precisa se preocupar apenas com o que esses bytes significam no momento exato em que estão sendo usados. O C moderno permite que os compiladores imponham restrições adicionais, mas o C inclui - e o C ++ retém - a capacidade de decompor um ponteiro em uma sequência de bytes, montar qualquer sequência de bytes que contenha os mesmos valores em um ponteiro e, em seguida, usá-lo para acessar o objeto anterior.
Embora essa capacidade possa ser útil - ou mesmo indispensável - em alguns tipos de aplicativos, um idioma que inclua essa capacidade será muito limitado em oferecer suporte a qualquer tipo de coleta de lixo útil e confiável. Se um compilador não souber tudo o que foi feito com os bits que compõem um ponteiro, ele não terá como saber se informações suficientes para reconstruir o ponteiro podem existir em algum lugar do universo. Como seria possível que essas informações fossem armazenadas de maneira que o computador não pudesse acessar, mesmo que soubesse delas (por exemplo, os bytes que compõem o ponteiro podem ter sido mostrados na tela por tempo suficiente para alguém escrever em um pedaço de papel), pode ser literalmente impossível para um computador saber se um ponteiro pode ser usado no futuro.
Uma peculiaridade interessante de muitas estruturas de coleta de lixo é que uma referência de objeto não é definida pelos padrões de bits contidos nela, mas pelo relacionamento entre os bits contidos na referência de objeto e outras informações mantidas em outro local. Em C e C ++, se o padrão de bits armazenado em um ponteiro identificar um objeto, esse padrão de bit identificará esse objeto até que o objeto seja destruído explicitamente. Em um sistema GC típico, um objeto pode ser representado por um padrão de bits 0x1234ABCD em um momento, mas o próximo ciclo do GC pode substituir todas as referências a 0x1234ABCD por referências a 0x4321BABE, e o objeto será representado pelo último padrão. Mesmo se alguém exibisse o padrão de bits associado a uma referência de objeto e depois o lesse novamente no teclado,
fonte
Toda a conversa técnica está complicando demais o conceito.
Se você colocar o GC no C ++ para toda a memória automaticamente, considere algo como um navegador da web. O navegador da web deve carregar um documento da web completo E executar scripts da web. Você pode armazenar variáveis de script da web na árvore de documentos. Em um documento BIG em um navegador com muitas guias abertas, isso significa que toda vez que o GC deve fazer uma coleção completa, também deve digitalizar todos os elementos do documento.
Na maioria dos computadores, isso significa que PAGE FAULTS ocorrerá. Portanto, a principal razão para responder à pergunta é que ocorrerão falhas na página. Você saberá isso como quando o seu PC começar a fazer muito acesso ao disco. Isso ocorre porque o GC deve tocar em muita memória para provar indicadores inválidos. Quando você tem um aplicativo de boa-fé usando muita memória, a necessidade de verificar todos os objetos em todas as coleções é prejudicada por causa dos PAGE FAULTS. Uma falha de página ocorre quando a memória virtual precisa ser lida novamente na RAM do disco.
Portanto, a solução correta é dividir um aplicativo nas partes que precisam de GC e nas que não precisam. No caso do exemplo do navegador da web acima, se a árvore de documentos foi alocada com malloc, mas o javascript foi executado com o GC, toda vez que o GC entra nele, ele apenas varre uma pequena parte da memória e todos os elementos PAGED OUT da memória por a árvore de documentos não precisa ser paginada novamente.
Para entender melhor esse problema, procure na memória virtual e como ela é implementada nos computadores. É tudo sobre o fato de que 2 GB estão disponíveis para o programa quando não há realmente muita memória RAM. Em computadores modernos com 2 GB de RAM para um sistema 32BIt, esse não é um problema, desde que apenas um programa esteja sendo executado.
Como um exemplo adicional, considere uma coleção completa que deve rastrear todos os objetos. Primeiro, você deve verificar todos os objetos acessíveis através de raízes. Segundo, verifique todos os objetos visíveis na etapa 1. Em seguida, verifique os destruidores em espera. Então vá para todas as páginas novamente e desligue todos os objetos invisíveis. Isso significa que muitas páginas podem ser trocadas e retornadas várias vezes.
Portanto, minha resposta para resumir é que o número de PAGE FAULTS que ocorrem como resultado de tocar em toda a memória faz com que o GC completo para todos os objetos em um programa seja inviável e, portanto, o programador deve ver o GC como uma ajuda para coisas como scripts e banco de dados funcionam, mas fazem coisas normais com o gerenciamento manual de memória.
E a outra razão muito importante, é claro, são as variáveis globais. Para que o coletor saiba que um ponteiro de variável global está no GC, seriam necessárias palavras-chave específicas e, portanto, o código C ++ existente não funcionaria.
fonte
RESPOSTA CURTA: Não sabemos como fazer a coleta de lixo de maneira eficiente (com pouco tempo e espaço sobrando) e corretamente o tempo todo (em todos os casos possíveis).
RESPOSTA LONGA: Assim como C, C ++ é uma linguagem de sistemas; isso significa que é usado quando você está escrevendo o código do sistema, por exemplo, sistema operacional. Em outras palavras, o C ++ é projetado, assim como o C, com o melhor desempenho possível como o principal alvo. O padrão do idioma não adicionará nenhum recurso que possa prejudicar o objetivo de desempenho.
Isso interrompe a pergunta: por que a coleta de lixo dificulta o desempenho? O principal motivo é que, quando se trata de implementação, nós [cientistas da computação] não sabemos como fazer a coleta de lixo com um mínimo de sobrecarga, para todos os casos. Portanto, é impossível para o compilador C ++ e o sistema de tempo de execução executar a coleta de lixo com eficiência o tempo todo. Por outro lado, um programador de C ++ deve conhecer seu design / implementação e é a melhor pessoa para decidir a melhor forma de fazer a coleta de lixo.
Por último, se controle (hardware, detalhes, etc.) e desempenho (tempo, espaço, energia etc.) não são as principais restrições, o C ++ não é a ferramenta de gravação. Outro idioma pode servir melhor e oferecer mais gerenciamento de tempo de execução [oculto], com a sobrecarga necessária.
fonte
Quando comparamos C ++ com Java, vemos que o C ++ não foi projetado com a Garbage Collection implícita em mente, enquanto o Java era.
Ter coisas como ponteiros arbitrários no estilo C não é ruim apenas para implementações de GC, mas também destruiria a compatibilidade com versões anteriores de uma grande quantidade de C ++ - código legado.
Além disso, o C ++ é uma linguagem que deve ser executada como executável independente, em vez de ter um ambiente de tempo de execução complexo.
Em suma: Sim, pode ser possível adicionar a Coleta de Lixo ao C ++, mas por uma questão de continuidade, é melhor não fazer isso.
fonte
Principalmente por duas razões:
O C ++ já oferece gerenciamento manual de memória, alocação de pilha, RAII, contêineres, ponteiros automáticos, ponteiros inteligentes ... Isso deve ser o suficiente. Os coletores de lixo são para programadores preguiçosos que não querem gastar 5 minutos pensando em quem deve possuir quais objetos ou quando os recursos devem ser liberados. Não é assim que fazemos as coisas em C ++.
fonte
Impor a coleta de lixo é realmente uma mudança de paradigma de baixo para alto nível.
Se você observar como as strings são tratadas em um idioma com coleta de lixo, você descobrirá que elas SOMENTE permitem funções de manipulação de strings de alto nível e não permitem acesso binário às strings. Simplificando, todas as funções de string verificam primeiro os ponteiros para ver onde está a string, mesmo se você estiver apenas desenhando um byte. Portanto, se você estiver executando um loop que processa cada byte em uma string em um idioma com coleta de lixo, ele deve calcular o local base mais o deslocamento para cada iteração, porque não pode saber quando a string foi movida. Então você tem que pensar em pilhas, pilhas, threads, etc., etc.
fonte