www.digitalmars.com Home | Search | D | Comments
Last update Sat Feb 25 11:48:34 2006
D
Language
Phobos
Comparisons

· Lexical
· Modules
· Declarations
· Types
· Properties
· Attributes
· Pragmas
· Expressions
· Statements
· Arrays
· Structs & Unions
· Classes
· Interfaces
· Enums
· Functions
· Operator Overloading
· Templates
· Mixins
· Contracts
· Conditional Compilation
· Handling errors
· Garbage Collection
· Memory Management
· Floating Point
· Inline Assembler
· Documentation Comments
· Interfacing To C
· Portability Guide
· Embedding D in HTML
· Named Character Entities
· Application Binary Interface

Coleta de Lixo

D é uma linguagem com total coleta de lixo. Isso significa que nunca é necessário liberar memória. Apenas alocar é necessário, e o coletor de lixo irá periodicamente retornar toda memória não usada para a piscina de memória disponível.

Programadores C e C++ acostumados a gerênciar alocação e desalocação de memória explícitamente serão provavelmente cépticos dos benifício e eficácia da coleta de lixo. Expirência com novos projetos escritos com coleta de lixo em mente, e convertendo projetos existentes para com coleta de lixo mostra que:

  • Programas com coleta de lixa são mais rápidos. Isso é contra-intuitivo, mas as razões são:
    • Referência contando é uma solução comum para resolver o problema de alocação de memória explícita. O código para implementar as operações de incremento e decremento sempre que atribuições são feitas é uma fonte de lerdeza. Esconder isso atrás de classes ponteiro inteligêntes não ajuda na velocidade. (Métodos de referência contando não são uma solução de maneira alguma, como referências circulares nunca são deletadas.)
    • Destrutores são usados para desalocar recursos adquiridos por um objeto. Para a maioria das classes, esses recursos são memória alocada. Com coleta de lixo, a maioria dos destrutores ficam vazios e podem ser totalmente descartados.
    • Todos aqueles destrutores liberando memória podem se tornar significantes quando objetos são alocados na pilha. Para cada um, alguns mecânismos devem ser estabelecidos então se uma exceção acontece, todos os destrutores são chamados em cada armação para soltar qualquer memória que eles seguram. Se destrutores se tornam irrelevantes, então não há necessidade de montar armações de pilha especiais para controlar exceções, e o código executa mais rápido.
    • Todo código necessário para liberar memória pode somar bastantes. O tamanho que um programa é, o menor é cache que ele é, mais páginas que ele faz, e a lerdeza que ele executa.
    • Coleta de lixo chuta certo quando memória fica apertada. quando memória não é apertada, o programa executa a toda velocidade e não gasta qualquer tempo liberando memória.
    • Coletores de lixo modernos são muito mais avançados que os antigos, mais lentos. Generational, copiar coletores elimina muito da eficiência de algorítmos e marcação e varredura.
    • Coletores de lixo modernos fazem compactação de heap. Compactação de heap tende a reduzir o número de páginas ativamente referênciadas por um programa, o que significa que acessoa à memória são provavelmente golpes no cache e menas trocas.
    • Programas com coleta de lixo não sofrem deterioração gradual devido a uma acumulação de vazamentos de memória.
  • Coletores de lixo corrigem memória não usada, portanto eles não sofrem com "vazamentos de memória" que podem causar aplicações executando muito tempo para gradualmente consumir mais e mais memória até que derrubem o sistema. Programas de GC têm termo de estabilidade mais longa.
  • Programas com coleta de lixo tem menos bugs com ponteiros difíceis de encontrar. Isso é porque não há nenhuma referência oscilando para liberar memória. Não há código para gerênciar memória explícitamente, conseqüentemente.
  • Programas com coleta de lixo são mais rápidos para desenvolver e depurar, porque não há necessidade de desenvolver, depurar, testar, ou manter o código de desalocação explícita.
  • Programas com coleta de lixo podem ser significantemente menores, porque não há código para gerênciar desalocação, e não há necessidade para manipuladores de exceção desalocar memória.
Coleta de lixo não é uma panácea. Há alguns lados ruins:
  • Não é previsível quando uma coleta é executada, então o programa pode pausar arbitrariamente.
  • O tempo que leva para uma coleta ser executada não é saltado. Enquanto na prática é muito rápido, não pode ser garantido.
  • Todos os threads diferentes do thread do coletor devem ser parados enquanto a coleta está em progresso.
  • Coletores de lixo podem manter alguma memória que um desalocador explícito não vai. Na prática, isso não é muito de um assunto já que desalocadores explícitos normalmente tem vazamentos de memória os causando a eventualmente usar mais memória, e porque desalocadores explícitos normalmente não devolvem memória desalocada para o sistema operacional de maneira nenhuma, ao invés de apenas develverem isso para sua própria piscina interna.
  • Coleta de lixo deveria ser implementada como um serviço básico do núcleo do sistema operacional.Mas já que não é, programas com coleta de lixo devem carregar com eles o a impleemtnação da coleta de lixo. Enquanto isso pode ser uma DLL compartilhada, ainda está lá.
Estes constrangimentos são enviados por técnicas esboçadas em Gerênciamento de Memória.

Como Coleta de Lixo Trabalha

A ser estrico...

Conectando Objetos com Coleta de Lixo comCódigo Estrangeiro

O coletor de lixo procura raízes em seu segmento de dados estático, e as pilhas e conteúdos de registradores em cada linha. Se a única raís do objeto é segurada fora dele, então o coletor perderá ela e a liberará memória.

Para evitar isso de acontecer,

  • Mantenha a raíz para o objeto na área em que o coletor faz buscas por raízes.
  • Realoque o objeto usando o código de armazenamento alocador do estrangeiro ou usando malloc/free da biblioteca C.

Ponteiros e o Coletor de Lixo

Ponteiros em D podem ser claramente divididos em duas categorias: aqueles que apontam para memória com coleta de lixo, e aqueles que não. Exemplos disso são ponteiros criados por chamadas à  malloc() do C, ponteiros recebidos de rotinas da biblioteca C, ponteiros para dados estáticos, ponteiros para objetos na pilha, etc. Para esses ponteiros, qualquer coisa que é ilegal em C pode ser feita com eles.

Para ponteiros e referências com coleta de lixo, porém, há algumas restrições. Estas restrições são secundárias, mas é pretendido que elas habilitem a flexibilidade máxima no projeto do coletor de lixo.

Comportamento indefinido:

  • Não xor ponteiros com outros valores, como o truque da lista linkada com xorusado em C.

  • Não use o truque xor para trocar dois valores.

  • Não armazene ponteiros em variáveis não-ponteiro usando elencos e outros truques.
    void* p;
    ...
    int x = cast(int)p; // erro: comportamento indefinido
    O coletor de lixo não procura tipos não-ponteiro por raízes.

  • Não tome vantagem do alinhamento dos ponteiros para armazenar bit flags nos bits de baixa ordem:
    p = cast(void*)(cast(int)p | 1); // erro: comportamento indefinido
  • Não armazene em ponteiros valores que podem apontar no heap do coletor de lixo:
    p = cast(void*)12345678; // error: comportamento indefinido
    Uma compactação do coletor de lixo pode mudar esse valor.

  • Não armazene valores mágicos em ponteiros, a não ser null.

  • Não escreva valores de ponteiros fora para o disco e leia-os novamente.

  • Não use valores de ponteiros para calcular uma função de hash. Uma cópia do coletor de lixo pode arbitrariamente mover objetos ao redor na memória, enquanto invalidando o valor hash computado.

  • Não dependa da ordenação dos ponteiros:
    if (p1 < p2) // erro: comportamento indefinido
    ...
    já que, novamente, o coletor de lixo pode mover objetos na memória.

  • Não some ou subtraia um offset para um ponteiros tal que o resultado aponte fora dos limites do objeto com coleta de lixo originalmente alocado.
    char* p = new char[10];
    char* q = p + 6; // ok
    q = p + 11; // erro: comportamento indefinido
    q = p - 1; // erro: comportamento indefinido
  • Não faça ponteiros desalinhados se esses ponteiros podem apontar no heap do gc, como:
    align (1) struct Foo
    {
    byte b;
    char* p; // ponteiro desalinhado
    }
    Ponteiros desalinhados podem ser usados se o hardware suportá-los e o ponteiro nunca for usado para apontar para o heap do gc.

  • Não use cópias de memória byte-a-byte para copiar valores de ponteiros. Isso pode resultar em condições intermediárias onde não há um ponteiro válidp, e se o gc pausa o thread em uma condição, ele pode corromper memória. Muitas implementações de memcpy() trabalharão desde que a implementação interna faça a cópia em pedaços alinhados maiores que ou iguais a um tamanho de ponteiro, mas já que esse tipo de implementação não é garantida pelo padrão C, use memcpy() somente com extremo cuidado.
Coisas que são seguras e podem ser feitas:
  • Use uma união para compartilhar armazenamento com um ponteiro:
    union U { void* ptr; int value }
    Usando tal união, porém, como um substituto para um cast(int) resultará em comportamento indefinido.

  • Um ponteiro para o começo de um objeto com coleta de lixo não precisa ser mantido  se um ponteiro para o interior do objeto existe.
    char[] p = new char[10];
    char[] q = p[3..6];
    // q é o bastante para segurar o object, não é preciso manter
    // p também.
Alguém pode evitar usar ponteiros de qualquer modo para muitas tarefas. D provê características fazendo muitos usos explícitos de ponteiros obsoletos, como objetos de referência, ordens dinâmicas, e coleta de lixo. Ponteiros são providos para se conectar com sucesso com APIs C e para alguns trabalhos de baixo nível.

Trabalhando com o Coletor de Lixo

Coleta de lixo não resolve todos os problemas de desalocação de memória. Por exemplo, se uma raíz para uma estrutura de dados grande é mantida, o coletor de lixo não pode reformá-la, mesmo se nunca mais for referida novamente. Para eliminar esse problema, é uma boa prática ajustar uma referência ou ponteiro para um objeto para nulo quando não for mais necessário.

Esse conselho se aplica somente à referências estáticas ou referências embutidas dentro de outros objetos. Não há muito ponto para tal armazenamento na pilha ser anulado, já que o coletor não escanea por raízas além do topo da pilha, e porque novos quadros da pilha são inicializados de qualquer modo.

Referências