c # recolección de basura

Directorio

 

1. Introducción

2. Basura

2. ¿Por qué usar GC?

3. Mecanismo de recogida de basura GC

1. Introducción

2. Generación

3. Recursos gestionados y recursos no gestionados

4. Para el método de liberación de recursos no administrados

5. Siente la diferencia entre los tres métodos a través del código


1. Introducción

El artículo anterior aprendió sobre los dos tipos de datos principales de los tipos de valor C # y los tipos de referencia y su contenido relacionado con la administración de memoria.

2. Basura

Los tipos .Net se dividen en dos categorías, uno es el tipo de valor y el otro es el tipo de referencia. El primero se asigna en la pila, y sale automáticamente de la pila a medida que se ejecuta el alcance de ejecución de la función, y no se requiere el reciclaje de GC ; el último se asigna en el montón (Heap), por lo que GC debe completar su liberación y reciclaje de memoria . La memoria ocupada por un objeto de tipo de referencia debe ser reclamada por el GC, y para cumplir con las condiciones de reciclaje, primero debe llamarse basura. Entonces, si .Net determina que un objeto de tipo de referencia es basura, el juicio de .Net es muy simple: siempre que se determine que no hay referencia a este objeto ni a los subobjetos que contiene, el sistema lo considera basura.

2. ¿Por qué usar GC?

      1. Mejorar la abstracción del desarrollo de software;

  2. El programador puede concentrarse en el problema real sin distraerse para manejar el problema de memoria;

  3. Puede aclarar la interfaz del módulo y reducir el acoplamiento entre los módulos;

  4. Reduzca en gran medida los errores causados ​​por el manejo inadecuado de la memoria;

  5. Haga que la gestión de la memoria sea más eficiente.

  En general, GC puede permitir a los programadores deshacerse de problemas complejos de memoria, mejorando así la velocidad, la calidad y la seguridad del desarrollo de software.

3. Mecanismo de recogida de basura GC

1. Introducción

 C #, como Java, es un lenguaje en el que el sistema recicla y libera recursos automáticamente. En el entorno C #, GC (Garbage Collect) se utiliza para reciclar los recursos del sistema. La recolección de basura GC se refiere principalmente a los recursos almacenados en el montón.

    Hay dos problemas con el mecanismo de GC .NET.
  Primero, GC no libera todos los recursos. No puede liberar automáticamente recursos no administrados.
  En segundo lugar, el GC no es en tiempo real, lo que provocará cuellos de botella e incertidumbres en el rendimiento del sistema. El programador no puede determinar la frecuencia de la limpieza del GC, y el CLR la controlará automáticamente. Cuando un objeto se marca como basura, el objeto no se puede recoger de inmediato.
  GC no es en tiempo real, lo que provocará cuellos de botella e incertidumbres en el rendimiento del sistema. Entonces, con la interfaz IDisposable , la interfaz IDisposable define el método Dispose , que utilizan los programadores para llamar explícitamente a liberar recursos no administrados. El uso de la declaración de uso puede simplificar la gestión de recursos.

2. Generación

El recolector de basura admite el envejecimiento de los objetos a través de generaciones, lo cual se recomienda pero no es obligatorio. Una generación es una unidad de objetos con edad relativa en la memoria. El código de objeto o edad o edad identifica a qué generación pertenece el objeto. En el ciclo de vida de una aplicación, los objetos creados más recientemente pertenecen a una generación más nueva y tienen un número de código más bajo que los objetos creados anteriormente. El código objeto en la última generación es 0.

En el nuevo objeto, primero debe buscar en la lista libre, encontrar el bloque de memoria más adecuado, asignar, ajustar la lista de bloqueo de memoria y fusionar los fragmentos. La nueva operación casi se puede completar en el tiempo O (1), agregando 1 al puntero superior. El principio de funcionamiento es: cuando el espacio restante en el montón administrado es insuficiente, o el generador 0 está lleno, el GC se ejecuta y comienza a reclamar memoria. Al comienzo de la recolección de basura, GC ajusta la compresión de la memoria de almacenamiento dinámico y los objetos se concentran en la parte superior. Cuando el GC escanea la basura, ocupará una cierta cantidad de tiempo de CPU. El algoritmo original del GC realmente escanea todo el montón, lo cual es ineficiente. El GC actual divide los objetos en el montón en tres generaciones. El último que ingresó al montón es la generación 0 (generación 0), seguido de la generación 1, generación 2. El primer GC solo escanea la generación 0. Si el espacio recuperado es suficiente para el uso actual, no hay necesidad de escanear otros objetos de generación. Proceso específico: cuando el programa se está ejecutando, hay objetos que deben almacenarse en el montón, GC creará la generación 0 (suponiendo que el tamaño del espacio sea 256K), los objetos se almacenarán en la generación 0, cuando el programa continúe ejecutándose, se ejecutará hasta el 0 El tamaño de la generación no es suficiente para almacenar el objeto. En este momento, se creará la primera generación (suponiendo que el espacio sea 10M), y el GC limpiará los "objetos basura" en la generación 0 y colocará los objetos "vivos" en la primera. Generación, en este momento, la generación 0 está vacía, se usa para almacenar nuevos objetos, cuando la generación 0 está llena, continuará realizando las operaciones anteriores, a medida que se ejecuta el programa, la 1ra generación no puede cumplir con los requisitos de almacenamiento, esto Se creará la segunda generación y el método de limpieza es el mismo que el anterior. Por lo tanto, GC es más eficiente en la creación de objetos que C ++ y no necesita escanear todo el espacio de almacenamiento dinámico. Mejora el rendimiento aportado por la estrategia de escaneo y la estrategia de gestión de memoria, que es suficiente para compensar el tiempo de CPU ocupado por el GC.

El sistema determina la recolección de basura por GC. El siguiente es el código de ejecución para la recuperación forzada (no use este método bajo circunstancias especiales, afectará la eficiencia del sistema y debilitará el papel del motor de optimización en el recolector de basura, y el recolector de basura puede determinar la operación de la basura El mejor momento para reciclar)

//对所有代进行垃圾回收。
GC.Collect();
//对指定的代进行垃圾回收。
GC.Collect(int generation); 
//强制在 System.GCCollectionMode 值所指定的时间对零代到指定代进行垃圾回收。
GC.Collect(int generation, GCCollectionMode mode); 

3. Recursos gestionados y recursos no gestionados

Recursos administrados: recursos que .NET puede recuperar automáticamente, principalmente en referencia a los recursos de memoria asignados en el montón administrado. El reciclaje de los recursos administrados no requiere intervención humana. Algunas bibliotecas de tiempo de ejecución .NET llaman al recolector de basura para su reciclaje.

Recursos no administrados: .NET no sabe cómo reciclar recursos. El tipo más común de recursos no administrados son los objetos que envuelven recursos del sistema operativo, como archivos, ventanas, conexiones de red, conexiones de bases de datos, pinceles, iconos, etc. Para tales recursos, el recolector de basura llamará al método Object.Finalize () al limpiar. De manera predeterminada, el método está vacío. Para los objetos no administrados, debe escribir código para reciclar los recursos no administrados en este método para que el recolector de basura pueda reciclar correctamente los recursos.

4. Para el método de liberación de recursos no administrados

.Net proporciona tres métodos, los tres más comunes , aproximadamente de la siguiente manera

1. Destructor

 public class cat
    {


       ~cat()
       {
           //析构函数语法
       }
    }

El constructor puede realizar ciertas operaciones al crear una instancia de objeto. El destructor es lo opuesto a la operación que se realiza cuando se crea el recurso después de reciclar el sistema. El recolector de basura llamará al destructor antes de reciclar el objeto, por lo que en el bloque de código de función Puede escribir código que libere recursos no administrados. El destructor no tiene valor de retorno, no tiene parámetros ni modificadores .

Hay dos tipos de basura en la memoria, uno necesita llamar al destructor del objeto y el otro no necesita ser llamado. Para el reciclaje que necesita llamar al destructor, debe completarse en dos pasos: el primer paso es llamar al destructor del objeto y el segundo paso es recuperar la memoria, pero debe tenerse en cuenta que estos dos pasos no se completan en el sondeo del GC, que requiere dos Sondeo

El GC solo puede llamar al destructor, por lo que es imposible determinar cuándo se llamará, por lo que no es muy razonable usarlo como liberación de recursos, porque la liberación de recursos no es oportuna; pero para evitar la fuga de recursos, después de todo lo llamará el GC. Por lo tanto, el destructor puede usarse como remedio.

2. Herede la interfaz IDisposable e implemente el método Dispose;

3. Método de cierre

 

El más utilizado para el método de eliminación y el método Cerrar está en la conexión de datos.

La diferencia entre los dos métodos, Cerrar y Eliminar, es que después de llamar al método Cerrar del objeto, el objeto puede reutilizarse; y para el método Eliminar, los recursos ocupados por este objeto deben marcarse como inútiles, es decir, esto El objeto se destruye, esperando que se recupere el GC, y ya no se puede usar.

 /// <summary>
        /// 执行查询,返回要查询的结果集
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="par_CommandType">sql语句的类型</param>
        /// <param name="par_SqlParameter">参数的类型</param>
        /// <returns>返回结果集</returns>
        public DataTable ExecuteTable(string sql,CommandType par_CommandType,params System.Data.SqlClient.SqlParameter [] par_SqlParameter)
        {
            DataTable datatable = new DataTable("dt");
            using(SqlConnection conn=new SqlConnection(connString))
            {
                using(SqlCommand command=new SqlCommand(sql,conn))
                {
                    try
                    {
                        //数据库连接
                        if(conn.State!=ConnectionState.Open)
                        {
                            conn.Open();
                        }
                        //sql语句类型的设置
                        command.CommandType = par_CommandType;
                        command.CommandTimeout = 36000;
                        //如果传入了参数,把其添加到命令对象中
                        if(par_SqlParameter!=null)
                        {
                            for(int i=command.Parameters.Count-1;i>=0;i--)
                            {
                                command.Parameters.RemoveAt(i);
                            }
                            command.Parameters.AddRange(par_SqlParameter);
                        }
                        //创建一个适配器对象,用以将结果填充到datatable中
                        SqlDataAdapter adapt = new SqlDataAdapter(command);
                        adapt.Fill(datatable);
                    }
                    catch(SqlException e)
                    {
                        throw new Exception(e.Message);
                    }
                    finally
                    {
                        command.Parameters.Clear();
                        command.Dispose();
                        conn.Close();
                    }
                }
            }
            return datatable;
        }

5. Siente la diferencia entre los tres métodos a través del código

   public class Program
    {
         static void Main(string[] args)
        {
            // Show destructor
           new Program().Create();
           Console.WriteLine("After created!");
          new Program().CallGC();
          Console.ReadKey();
        }
        private void Create()
        {
            DisposeClass myClass = new DisposeClass();
        }
        private void CallGC()
        {
            GC.Collect();
        }
    }
    public class DisposeClass : IDisposable
    {
        public void Close()
        {
            Console.WriteLine("Close called!");
        }
        ~DisposeClass()
        {
            Console.WriteLine("Destructor called!");
        }
        #region IDisposable Members
        public void Dispose()
        {
            Console.WriteLine("Dispose called!");
        }
        #endregion
    }

Cuando se llama al método Create, no llama directamente al destructor, sino que espera a que la pantalla llame a GC.Collect antes de llamarlo.

 static void Main(string[] args)
       {
           using (DisposeClass myClass = new DisposeClass())
           {
               //other operation here
           }
           Console.ReadKey();
       }
    }
    public class DisposeClass : IDisposable
    {
        public void Close()
        {
            Console.WriteLine("Close called!");
        }
        ~DisposeClass()
        {
            Console.WriteLine("Destructor called!");
        }
        #region IDisposable Members
        public void Dispose()
        {
            Console.WriteLine("Dispose called!");
        }
        #endregion
    }

Agregué Console.WriteLine () al código, por lo que solo vi una línea de visualización. De hecho, el código no se ha completado en este momento. Si se elimina esta línea de código, el destructor se ejecutará antes de que finalice el proceso.

Para Dispose, también debe llamar a la pantalla, pero puede usar la palabra clave using para objetos de tipos que heredan IDisposable, de modo que el método Dispose del objeto se invocará automáticamente después de que esté fuera del rango de uso.

Entonces, para la implementación de Dispose del tipo DisposeClass anterior, de hecho, GC también necesita llamar al destructor del objeto. Normalmente, la función Dispose debe decirle a GC llamando a GC.SuppressFinalize (this) para que no necesite llamar al análisis del objeto Constructor. Entonces el DisposeClass reescrito es el siguiente:

public class DisposeClass : IDisposable
{
    public void Close()
    {
        Debug.WriteLine("Close called!");
    }
    ~DisposeClass()
    {
        Debug.WriteLine("Destructor called!");
    }
    #region IDisposable Members
    public void Dispose()
    {
        Debug.WriteLine("Dispose called!");
        //不在执行析构函数
        GC.SuppressFinalize( this );
    }
    #endregion
} 

Referencia

https://www.jb51.net/article/41819.htm

https://blog.csdn.net/u011555996/article/details/102554037

https://www.cnblogs.com/qtiger/p/11177157.html

https://blog.csdn.net/chenyu964877814/article/details/7461948

https://www.cnblogs.com/wwj1992/p/8387360.html

30 artículos originales publicados · Me gusta1 · Visitas1158

Supongo que te gusta

Origin blog.csdn.net/chunchunlaila/article/details/105565399
Recomendado
Clasificación