· 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
|