Gerenciamento automático de memória
Gerenciamento automático de memória é um dos serviços que o Common Language Runtime fornece durante a Execução Gerenciada.O coletor de lixo do Common Language Runtime gerencia a alocação e liberação de memória para um aplicativo.Para desenvolvedores, isso significa que você não tem que escrever código para executar tarefas de gerenciamento de memória quando você desenvolver aplicativos gerenciados.Gerenciamento automático de memória pode eliminar problemas comuns, como esquecer de liberar um objeto e causar um vazamento de memória, ou tentar acessar a memória de um objeto que já tinha sido liberado.Esta seção descreve como o coletor de lixo aloca e libera memória.
Alocando memória
Quando você inicializa um novo processo, o tempo de execução reserva uma região contígua de espaço de endereço para o processo.Este espaço de endereço reservado é chamado de heap gerenciada.A heap gerenciada mantém um ponteiro para o endereço onde o próximo objeto da heap será alocado.Inicialmente, esse ponteiro é definido como Endereço básico da heap gerenciada.Todos os tipos de referência são alocados na heap gerenciada.Quando um aplicativo cria o primeiro tipo de referência, é alocada memória para o tipo no Endereço básico da heap gerenciada.Quando o aplicativo cria o próximo objeto, o coletor de lixo aloca memória para ele no espaço de endereço imediatamente após o primeiro objeto.Desde que exista espaço de endereço disponível, o coletor de lixo continua a alocar espaço para novos objetos dessa maneira.
Alocar memória de heap gerenciada é mais rápido que a alocação de memória não gerenciada.Pelo fato de que o Runtime aloca memória para um objeto adicionando um valor a um ponteiro, ele é quase tão rápido quanto a alocação de memória da pilha (stack).Além disso, porque novos objetos que são alocados consecutivamente são armazenados contiguamente na heap gerenciada, um aplicativo pode acessar os objetos muito rapidamente.
Liberando memória
O mecanismo otimizado do coletor de lixo determina o melhor momento para executar uma coleta com base nas alocações sendo feitas.Quando o coletor de lixo executa uma coleta, ele libera a memória dos objetos que não estão sendo usados pelo aplicativo.Ele determina quais objetos não estão mais sendo usados pelo exame das raízes do aplicativo.Cada aplicativo tem um conjunto de raízes.Cada raiz refere-se a um objeto na heap gerenciada ou é definida como nula.Raízes do aplicativo incluem ponteiros para objetos globais e estáticos, variáveis locais e parâmetros de objetos de referência na pilha de um segmento, e registro da CPU.O coletor de lixo tem acesso à lista de raízes ativas mantidas pelo runtime e peloCompilador Just-In-Time (JIT).Usando essa lista, ele examina as raízes do aplicativo, e no processo cria um gráfico que contém todos os objetos que possam ser alcançados a partir as raízes.
Objetos que não estão no gráfico são inacessíveis a partir das raízes do aplicativo.O coletor de lixo considera como lixo os objetos inacessíveis e irá liberar a memória alocada para eles.Durante uma coleta, o coletor de lixo examina a heap gerenciada, procurando pelos blocos de espaço de endereço ocupados por objetos inacessíveis.Na medida em que ele descobre cada objeto inacessível, ele usa uma função de cópia de memória para compactar os objetos acessíveis na memória, liberando os blocos de espaços endereço alocados para objetos inacessíveis.Uma vez que a memória para dos objetos acessíveis tiver sido compactada, o coletor de lixo faz as correções de ponteiros necessárias raízes de forma que as raízes do aplicativo apontem para os objetos em seus novos locais.Ele também posiciona o ponteiro da heap gerenciadas após o último objeto acessível.Observe que memória é compactada somente se uma coleta descobre um número significativo de objetos inacessíveis.Se todos os objetos na heap gerenciada sobrevivem a uma coleta, não há necessidade de compactação de memória.
Para melhorar o desempenho, o tempo de execução aloca memória para objetos grandes em um heap separado.O coletor de lixo automaticamente libera a memória para objetos grandes.No entanto, para evitar mover objetos grandes na memória, essa memória não é compactada.
Gerações e Desempenho
Para otimizar o desempenho do coletor de lixo, a heap gerenciada está dividida em três gerações: 0, 1 e 2.Algoritmo de coleta de lixo do tempo de execução se baseia em várias generalizações que indústria de software do computador tem descoberto para serem verdadeiras por experiências com esquemas de coleta de lixo.Primeiro, é mais rápido compactar a memória para uma parte do heap gerenciado que para o heap gerenciado inteiro.Em segundo lugar, objetos mais recentes terão vidas úteis menores e objetos mais antigos objetos terão vidas úteis maiores.Finalmente, objetos mais recentes tendem a ser relacionados entre si e acessados pelo aplicativo aproximadamente ao mesmo tempo.
O coletor de lixo do Runtime armazena novos objetos na geração 0.Objetos criados com antecedência no tempo de vida do aplicativo que sobrevivem a coletas são promovidos e armazenados em gerações 1 e 2.O processo de promoção do objeto é descrito posteriormente neste tópico.Porque é mais rápido compactar uma parte da heap gerenciada que a heap inteira, este esquema permite que o coletor de lixo libere a memória em uma geração específica em vez liberar a memória para toda a memória gerenciada a cada vez que ele executa uma coleta.
Na verdade, o coletor de lixo executa uma coleta quando a geração 0 está cheia.Se um aplicativo tentar criar um novo objeto quando a geração 0 está cheia, o coletor de lixo descobre que não existe nenhum espaço de endereço restante na geração 0 para alocar para o objeto.O coletor de lixo executa uma coleta em uma tentativa de liberar espaço de endereço na geração 0 para o objeto.O coletor de lixo inicia examinando os objetos na geração 0 em vez de todos os objetos na heap gerenciada.Isso é a abordagem mais eficiente, porque novos objetos costumam ter tempos de vida curtos, e é esperado que muitos dos objetos na geração 0 não estejam mais em uso mais pelo aplicativo quando uma coleta é executada.Além disso, uma única coleta de geração 0 frequentemente recupera memória suficiente para permitir ao aplicativo continuar criando novos objetos.
Após o coletor de lixo executar uma coleta de geração 0, ele compacta a memória para os objetos acessíveis conforme explicado anteriormente neste tópico em Liberando memória.O coletor de lixo então promove esses objetos e considera esta parte da heap gerenciada como geração 1.Pelo fato de que objetos que sobrevivem a coletas costumam ter vidas úteis mais longas, faz sentido promovê-los para uma geração superior.Como resultado, o coletor de lixo não tem que reexaminar os objetos em gerações 1 e 2 sempre que ele executa uma coleta de geração 0.
Após o coletor de lixo executar sua primeira coleta de geração 0 e promover os objetos acessíveis na geração 1, ele considera o resto da heap gerenciada como geração 0.Ele continua a alocar memória para novos objetos na geração 0 até que a geração 0 esteja cheia e que seja necessário executar outra coleta.Nesse ponto, o mecanismo de otimização do coletor de lixo determina se ele é necessário examinar os objetos em gerações mais antigas.Por exemplo, se uma coleta de geração 0 não recupera memória suficiente para o aplicativo concluir sua tentativa de criar um novo objeto de forma bem sucedida, o coletor de lixo pode executar uma coleta de geração 1, e depois de geração 0.Se isso não recuperar memória suficiente, o coletor de lixo pode executar uma coleta de gerações 2, 1, e 0.Após cada coleta, o coletor de lixo compacta os objetos acessíveis na geração 0 e promove-os para geração 1.Objetos na geração 1 que sobrevivem a coletas são elevados para geração 2.Como o coletor de lixo oferece suporte somente a três gerações, objetos na geração 2 que sobrevivem a uma coleta permanecem na geração 2 até que eles sejam determinados como inalcançáveis em uma coleta futura.
Liberando Memória para Recursos não Gerenciados
Para a maioria dos objetos que seu aplicativo cria, você pode confiar no coletor de lixo para executar automaticamente as tarefas de gerenciamento de memória necessárias.Entretanto, recursos não gerenciados requerem limpeza explícita.O tipo mais comum de recursos não gerenciados é um objeto que envolve um recurso do sistema operacional, como um identificador de arquivo, identificador de janela ou conexão de rede.Embora o coletor de lixo seja capaz de controlar o tempo de vida de um objeto gerenciado que encapsula um recurso não gerenciado, ele não tem conhecimento específico sobre como limpar o recurso.Quando você cria um objeto que encapsula um recurso não gerenciado, é recomendável que você forneça o código necessário para limpar o recurso não gerenciados em um método público Dispose.Ao fornecer um método Dispose , você permite que usuários do seu objeto liberem, explicitamente, sua memória quando elas estiverem concluído com o objeto.Quando você usa um objeto que encapsula um recurso não gerenciados, você deve estar ciente das Dispose e chamá-las conforme necessário.Para obter mais informações sobre a limpeza de recursos não gerenciados e um exemplo de um padrão de design para implementar Dispose, consulte Coleta de Lixo.
Consulte também
Tarefas
Exemplo de tecnologia de coleta de Lixo
Conceitos
Processo de Execução Gerenciada