Inscreva-se agora

* Você receberá as últimas notícias e atualizações sobre suas celebridades favoritas!

Postagens em alta

Blog

Gerenciamento Automático de Memória no C#: Entendendo o Coletor de Lixo
Aprenda C#

Gerenciamento Automático de Memória no C#: Entendendo o Coletor de Lixo 

O C# utiliza um sistema de gerenciamento automático de memória, o que elimina a necessidade dos desenvolvedores alocarem e liberarem manualmente a memória ocupada pelos objetos. Esse gerenciamento é feito por um coletor de lixo (garbage collector), que cuida do ciclo de vida de um objeto, desde sua criação até sua liberação. Neste artigo, vamos explorar como o C# gerencia a memória de forma eficiente.

O Ciclo de Vida de um Objeto

No C#, o ciclo de vida de um objeto passa por várias etapas controladas pelo coletor de lixo:

  1. Criação do Objeto: Quando um objeto é criado, a memória é alocada, o construtor é executado, e o objeto é considerado “vivo”.
  2. Objeto Inacessível: Se o objeto ou seus campos de instância não puderem ser acessados por nenhuma execução subsequente, ele é considerado “não mais em uso” e se torna elegível para finalização.
  3. Finalização: Em um momento indeterminado, o finalizador (se houver) do objeto é executado. Isso ocorre uma única vez, salvo se APIs específicas alterarem esse comportamento.
  4. Elegível para Coleta: Após a execução do finalizador, o objeto se torna elegível para coleta se ele e seus campos não puderem mais ser acessados.
  5. Coleta de Lixo: Finalmente, o coletor de lixo libera a memória associada ao objeto em algum momento posterior.

Como o Coletor de Lixo Funciona?

O coletor de lixo do C# monitora o uso de objetos e toma decisões de gerenciamento de memória, como:

  • Onde alocar um novo objeto na memória.
  • Quando realocar um objeto.
  • Quando um objeto se torna inacessível e pode ser removido.

Embora o coletor de lixo tenha ampla liberdade para decidir quando coletar objetos, os desenvolvedores podem influenciar esse comportamento através de métodos estáticos da classe System.GC.

Exemplo de Coleta de Lixo:

class A
{
    ~A()
    {
        Console.WriteLine("Finalizando instância de A");
    }
}

class B
{
    object Ref;

    public B(object o)
    {
        Ref = o;
    }

    ~B()
    {
        Console.WriteLine("Finalizando instância de B");
    }
}

class Teste
{
    static void Main()
    {
        B? b = new B(new A());
        b = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

Neste exemplo, as instâncias de A e B tornam-se elegíveis para coleta de lixo quando a variável b é atribuída a null. O coletor de lixo então coleta os objetos, mas a ordem de finalização não é garantida. O resultado pode ser:

Finalizando instância de A
Finalizando instância de B

Ou

Finalizando instância de B
Finalizando instância de A

Finalizadores e Acessibilidade

Um detalhe importante é que, mesmo após um objeto ser finalizado, ele pode tornar-se acessível novamente. Um exemplo disso ocorre quando o finalizador de um objeto chama um método de outro objeto, tornando-o “vivo” novamente.

Exemplo:

class A
{
    ~A()
    {
        Console.WriteLine("Finalizando instância de A");
    }

    public void F()
    {
        Console.WriteLine("A.F");
        Teste.RefA = this;
    }
}

class B
{
    public A? Ref;

    ~B()
    {
        Console.WriteLine("Finalizando instância de B");
        Ref?.F();
    }
}

class Teste
{
    public static A? RefA;
    public static B? RefB;

    static void Main()
    {
        RefB = new B();
        RefA = new A();
        RefB.Ref = RefA;

        RefB = null;
        RefA = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();

        if (RefA != null)
        {
            Console.WriteLine("RefA não é nulo");
        }
    }
}

Nesse exemplo, o finalizador de B chama um método de A, o que faz com que A se torne acessível novamente. O programa gera a seguinte saída:

Finalizando instância de A
Finalizando instância de B
A.F
RefA não é nulo

Conclusão

O gerenciamento automático de memória no C# alivia o desenvolvedor da responsabilidade de gerenciar a alocação e liberação de memória manualmente. O coletor de lixo faz esse trabalho de forma eficiente, embora seja possível controlar parcialmente seu comportamento através da classe System.GC. Entender o funcionamento do coletor de lixo é essencial para garantir que o código seja eficiente e evite problemas de memória.

Posts relacionados

Deixe um comentário


Campos obrigatórios são marcado com *