Por que os indicadores inteligentes de contagem de referência são tão populares?

51

Como eu posso ver, os ponteiros inteligentes são usados extensivamente em muitos projetos C ++ do mundo real.

Embora alguns ponteiros inteligentes sejam obviamente benéficos para suportar RAII e transferências de propriedade, também existe uma tendência de usar ponteiros compartilhados por padrão , como uma forma de "coleta de lixo" , para que o programador não tenha que pensar muito na alocação.

Por que os indicadores compartilhados são mais populares do que a integração de um coletor de lixo adequado, como o Boehm GC ? (Ou você concorda em tudo, que eles são mais populares que os GCs reais?)

Eu sei de duas vantagens dos GCs convencionais sobre a contagem de referência:

  • Algoritmos de GC convencionais não têm problemas com ciclos de referência .
  • A contagem de referência é geralmente mais lenta do que um GC adequado.

Quais são os motivos para usar os ponteiros inteligentes de contagem de referência?

    
por Miklós Homolya 14.08.2013 / 04:28
fonte

5 respostas

57

Algumas vantagens da contagem de referência sobre a coleta de lixo:

  1. Baixa sobrecarga. Coletores de lixo podem ser bastante intrusivos (por exemplo, fazendo seu programa congelar em momentos imprevisíveis enquanto um ciclo de coleta de lixo processa) e bastante intensivo de memória (por exemplo, a memória do processo aumenta desnecessariamente em muitos megabytes antes que a coleta de lixo seja ativada)

  2. Comportamento mais previsível. Com a contagem de referência, você tem a garantia de que seu objeto será liberado no instante em que a última referência a ele desaparecer. Com a coleta de lixo, por outro lado, seu objeto será liberado "em algum momento", quando o sistema se aproximar dele. Para a RAM, isso geralmente não é um grande problema em desktops ou servidores pouco carregados, mas para outros recursos (por exemplo, identificadores de arquivo), é necessário que eles sejam fechados o mais rápido possível para evitar possíveis conflitos mais tarde.

  3. Mais simples. A contagem de referência pode ser explicada em poucos minutos e implementada em uma ou duas horas. Coletores de lixo, especialmente aqueles com desempenho decente, são extremamente complexos e muitas pessoas não os entendem.

  4. Padrão. O C ++ inclui contagem de referência (via shared_ptr) e amigos no STL, o que significa que a maioria dos programadores de C ++ está familiarizada com ele e a maioria dos códigos C ++ funcionarão com ele. No entanto, não existe coletor de lixo C ++ padrão, o que significa que você deve escolher um e esperar que ele funcione bem para o seu caso de uso - e se isso não acontecer, é problema seu corrigir, não o idioma.

Quanto às desvantagens alegadas da contagem de referência - não detectar ciclos é um problema, mas um que eu nunca encontrei pessoalmente nos últimos dez anos usando contagem de referência. A maioria das estruturas de dados é naturalmente acíclica, e se você se deparar com uma situação em que você precisa de referências cíclicas (por exemplo, ponteiro pai em um nó de árvore), basta usar um weak_ptr ou um ponteiro C bruto para "direção inversa". Contanto que você esteja ciente do possível problema quando estiver projetando suas estruturas de dados, não é um problema.

Quanto ao desempenho, nunca tive um problema com o desempenho da contagem de referência. Eu tive problemas com o desempenho da coleta de lixo, em particular os congelamentos aleatórios que o CG pode incorrer, aos quais a única solução ("não alocar objetos") poderia também ser reformulada como "não use o GC" .

    
por 14.08.2013 / 04:48
fonte
26

Para obter um bom desempenho de um GC, o GC precisa ser capaz de mover objetos na memória. Em uma linguagem como o C ++, onde você pode interagir diretamente com os locais da memória, isso é praticamente impossível. (O Microsoft C ++ / CLR não conta porque introduz uma nova sintaxe para ponteiros gerenciados por GC e, portanto, é efetivamente um idioma diferente.)

O Boehm GC, embora seja uma idéia bacana, é realmente o pior dos dois mundos: você precisa de um malloc () que é mais lento que um bom GC, e assim você perde o comportamento determinístico de alocação / desalocação sem o aumento de desempenho correspondente um GC geracional. Além disso, é por necessidade conservadora, por isso não irá necessariamente recolher todo o seu lixo de qualquer maneira.

Um GC bom e bem ajustado pode ser ótimo. Mas em uma linguagem como o C ++, os ganhos são mínimos e os custos muitas vezes não valem a pena.

Será interessante ver, no entanto, como o C ++ 11 se torna mais popular, se lambdas e semânticas de captura começam a levar a comunidade C ++ aos mesmos tipos de alocação e problemas de tempo de vida que causaram a comunidade Lisp a inventar os GCs. em primeiro lugar.

Veja também minha resposta a uma pergunta relacionada no StackOverflow .

    
por 14.08.2013 / 09:04
fonte
4

As I can see, smart pointers are used extensively in many real-world C++ projects.

É verdade, mas, objetivamente, a grande maioria do código é agora escrita em linguagens modernas com coletores de lixo de rastreamento.

Though some kind of smart pointers are obviously beneficial to support RAII and ownership transfers, there is also a trend of using shared pointers by default, as a way of "garbage collection", so that the programmer do not have to think about allocation that much.

Essa é uma má ideia, porque você ainda precisa se preocupar com os ciclos.

Why are shared pointers more popular than integrating a proper garbage collector like Boehm GC? (Or do you agree at all, that they are more popular than actual GCs?)

Oh, uau, há tantas coisas erradas na sua linha de pensamento:

  1. O GC de Boehm não é um GC "adequado" em nenhum sentido da palavra. É verdadeiramente horrível. É conservador, portanto, vaza e é ineficiente por design. Consulte: link

  2. Ponteiros compartilhados são, objetivamente, nem de perto tão populares quanto o GC, porque a grande maioria dos desenvolvedores está usando linguagens GC'd agora e não precisam de ponteiros compartilhados. Basta olhar para Java e Javascript no mercado de trabalho em comparação com o C ++.

  3. Você parece estar restringindo a consideração ao C ++ porque, eu suponho, você acha que o GC é um problema tangencial. Não é (a única forma de obter um GC decente é projetar o idioma e a VM para ele desde o início), então você está introduzindo o viés de seleção. As pessoas que realmente querem a coleta de lixo adequada não aderem ao C ++.

What are the reasons for using reference-counting smart pointers?

Você está restrito ao C ++, mas gostaria de ter um gerenciamento automático de memória.

    
por 26.01.2016 / 22:10
fonte
3

No MacOS X e iOS, e com desenvolvedores usando Objective-C ou Swift, a contagem de referências é popular porque é tratada automaticamente, e o uso de coleta de lixo diminuiu consideravelmente, já que a Apple não suporta mais (me disseram que os aplicativos que usam coleta de lixo irão quebrar na próxima versão do MacOS X, e a coleta de lixo nunca foi implementada no iOS). Na verdade, duvido seriamente que já houvesse muito software usando a coleta de lixo quando estava disponível.

A razão para se livrar da coleta de lixo: nunca funcionou de forma confiável em um ambiente no estilo C, onde os ponteiros poderiam "escapar" para áreas não acessíveis pelo coletor de lixo. A Apple acredita firmemente e acredita que a contagem de referência é mais rápida. (Você pode fazer qualquer reclamação aqui sobre velocidade relativa, mas ninguém conseguiu convencer a Apple). E no final, ninguém usou a coleta de lixo.

A primeira coisa que qualquer desenvolvedor de MacOS X ou iOS aprende é como lidar com ciclos de referência, então isso não é um problema para um desenvolvedor real.

    
por 27.01.2016 / 01:53
fonte
2

A maior desvantagem da coleta de lixo em C ++ é a impossibilidade de acertar:

  • Em C ++, os ponteiros não vivem em sua própria comunidade murada, eles são misturados com outros dados. Assim, você não pode distinguir um ponteiro de outros dados que, por acaso, possuem um padrão de bits que pode ser interpretado como um ponteiro válido.

    Consequência: Qualquer coletor de lixo C ++ irá vazar objetos que devem ser coletados.

  • Em C ++, você pode fazer aritmética de ponteiro para derivar ponteiros. Assim, se você não encontrar um ponteiro para o início de um bloco, isso não significa que esse bloco não possa ser referenciado.

    Consequência: Qualquer coletor de lixo C ++ deve levar em conta esses ajustes, tratando qualquer seqüência de bits que aponte para qualquer lugar dentro de um bloco, incluindo logo após o final, como um ponteiro válido que referencia o bloco.

    Nota: Nenhum coletor de lixo C ++ pode manipular código com truques como estes:

    int* array = new int[7];
    array--;    //undefined behavior, but people may be tempted anyway...
    for(int i = 1; i <= 7; i++) array[i] = i;
    

    Verdade, isso invoca um comportamento indefinido. Mas algum código existente é mais inteligente do que é bom para ele e pode desencadear a desalocação preliminar de um coletor de lixo.

por 26.01.2016 / 22:34
fonte