yugioh traduccion invocar funciona example espaƱol delegados como collector clean c# .net garbage-collection

c# - traduccion - garbage collector yugioh



Es correcto usar GC.Collect(); GC.WaitForPendingFinalizers();? (6)

Hay un punto que se puede hacer que es muy fácil de entender: hacer que GC ejecute automáticamente limpia muchos objetos por ejecución (por ejemplo, 10000). Llamarlo después de cada destrucción limpia alrededor de un objeto por ejecución.

Debido a que GC tiene una gran sobrecarga (necesita detener e iniciar subprocesos, necesita escanear todos los objetos vivos) es altamente preferible realizar llamadas por lotes.

Además, ¿qué bien podría salir de limpiar después de cada objeto? ¿Cómo podría ser esto más eficiente que el procesamiento por lotes?

Comencé a revisar algunos códigos en un proyecto y encontré algo como esto:

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

Esas líneas generalmente aparecen en métodos concebidos para destruir el objeto bajo la razón de aumentar la eficiencia. He hecho estas observaciones:

  1. Para llamar a la recolección de basura explícitamente en la destrucción de cada objeto disminuye el rendimiento porque hacerlo no tiene en cuenta si es absolutamente necesario para el rendimiento CLR.
  2. Al llamar a esas instrucciones en ese orden, se destruye cada objeto solo si se finalizan otros objetos. Por lo tanto, un objeto que podría ser destruido independientemente tiene que esperar la destrucción de otro objeto sin una necesidad real.
  3. Puede generar un punto muerto (ver: esta pregunta )

¿Son 1, 2 y 3 verdad? ¿Puedes dar alguna referencia apoyando tus respuestas?

Aunque estoy casi seguro de mis comentarios, debo ser claro en mis argumentos para explicar a mi equipo por qué esto es un problema. Esa es la razón por la que estoy pidiendo confirmación y referencia.


He usado esto solo una vez: para limpiar la caché del lado del servidor de los documentos de Crystal Report. Ver mi respuesta en la excepción de Crystal Reports: se ha alcanzado el límite máximo de trabajos de procesamiento de informes configurado por el administrador del sistema

Los WaitForPendingFinalizers fueron particularmente útiles para mí, ya que a veces los objetos no se limpiaban correctamente. Teniendo en cuenta el rendimiento relativamente lento del informe en una página web, cualquier pequeño retraso de GC fue insignificante, y la mejora en la gestión de la memoria me dio un servidor más feliz en general.


La respuesta corta es: sácalo. Ese código casi nunca mejorará el rendimiento o el uso de la memoria a largo plazo.

Todos tus puntos son ciertos. ( Puede generar un interbloqueo; eso no significa que siempre lo hará ). Llamar a GC.Collect() recopilará la memoria de todas las generaciones de GC. Esto hace dos cosas.

  • Se recopila a través de todas las generaciones en todo momento , en lugar de lo que el GC hará por defecto, que es solo recopilar una generación cuando está llena. El uso típico hará que Gen0 recolecte (aproximadamente) diez veces más seguido que Gen1, que a su vez recolecta (aproximadamente) diez veces más que Gen2. Este código recogerá todas las generaciones cada vez. La colección Gen0 suele ser inferior a 100 ms; Gen2 puede ser mucho más largo.
  • Promueve objetos no coleccionables a la siguiente generación. Es decir, cada vez que fuerce una colección y aún tenga una referencia a algún objeto, ese objeto será promovido a la siguiente generación. Por lo general, esto sucederá relativamente raramente, pero un código como el siguiente lo forzará con mucha más frecuencia:

    void SomeMethod() { object o1 = new Object(); object o2 = new Object(); o1.ToString(); GC.Collect(); // this forces o2 into Gen1, because it''s still referenced o2.ToString(); }

Sin un GC.Collect() , estos dos elementos se recopilarán en la próxima oportunidad. Con la colección como writte, o2 terminará en Gen1, lo que significa que una colección Gen0 automatizada no liberará esa memoria.

También vale la pena notar un horror aún mayor: en el modo DEBUG, el GC funciona de manera diferente y no recuperará ninguna variable que aún esté en su alcance (incluso si no se usa más adelante en el método actual). Por lo tanto, en el modo DEBUG, el código anterior ni siquiera recopilaría o1 al llamar a GC.Collect , por lo que se o1 y o2 . Esto podría llevar a un uso de memoria muy errático e inesperado al depurar el código. (Artículos como this resaltan este comportamiento.)

EDIT: habiendo probado este comportamiento, algo de ironía real: si tiene un método como este:

void CleanUp(Thing someObject) { someObject.TidyUp(); someObject = null; GC.Collect(); GC.WaitForPendingFinalizers(); }

... entonces NO liberará explícitamente la memoria de algún Objeto, incluso en el modo LIBERACIÓN: lo promoverá en la próxima generación de GC.


Nos hemos encontrado con problemas similares a @Grzenio; sin embargo, estamos trabajando con arreglos bidimensionales mucho más grandes, del orden de 1000x1000 a 3000x3000, esto es en un servicio web.

Agregar más memoria no siempre es la respuesta correcta, debe comprender su código y el caso de uso. Sin recolección de GC, requerimos 16-32 gb de memoria (dependiendo del tamaño del cliente). Sin él, requeriríamos 32-64 gb de memoria, e incluso entonces no hay garantías de que el sistema no sufra. El recolector de basura .NET no es perfecto.

Nuestro servicio web tiene un caché en memoria del orden de 5 a 50 millones de cadenas (~ 80-140 caracteres por par de clave / valor según la configuración), además, con cada solicitud del cliente, construiremos 2 matrices, una de doble, una de boolean que luego pasaron a otro servicio para hacer el trabajo. Para una "matriz" de 1000x1000 (matriz bidimensional) esto es ~ 25 mb, por solicitud . El booleano diría qué elementos necesitamos (según nuestro caché). Cada entrada de caché representa una "celda" en la "matriz".

El rendimiento del caché se degrada dramáticamente cuando el servidor tiene una utilización de memoria de más del 80% debido a la paginación.

Lo que descubrimos es que, a menos que realicemos una GC explícitamente, el recolector de basura .net nunca ''limpiaría'' las variables transitorias hasta que estuviéramos en el rango de 90-95%, punto en el que el rendimiento del caché se había degradado drásticamente.

Dado que el proceso descendente a menudo tomó una larga duración (3-900 segundos), el impacto de rendimiento de una recopilación de GC fue despreciable (3-10 segundos por recopilación). Iniciamos esta recopilación después de que ya habíamos devuelto la respuesta al cliente.

En última instancia, hicimos los parámetros del GC configurables, también con .net 4.6 hay otras opciones. Aquí está el código .net 4.5 que usamos.

if (sinceLastGC.Minutes > Service.g_GCMinutes) { Service.g_LastGCTime = DateTime.Now; var sw = Stopwatch.StartNew(); long memBefore = System.GC.GetTotalMemory(false); context.Response.Flush(); context.ApplicationInstance.CompleteRequest(); System.GC.Collect( Service.g_GCGeneration, Service.g_GCForced ? System.GCCollectionMode.Forced : System.GCCollectionMode.Optimized); System.GC.WaitForPendingFinalizers(); long memAfter = System.GC.GetTotalMemory(true); var elapsed = sw.ElapsedMilliseconds; Log.Info(string.Format("GC starts with {0} bytes, ends with {1} bytes, GC time {2} (ms)", memBefore, memAfter, elapsed)); }

Después de volver a escribir para usar con .net 4.6, dividimos el recolector de basura en 2 pasos: una recopilación simple y una compacta.

public static RunGC(GCParameters param = null) { lock (GCLock) { var theParams = param ?? GCParams; var sw = Stopwatch.StartNew(); var timestamp = DateTime.Now; long memBefore = GC.GetTotalMemory(false); GC.Collect(theParams.Generation, theParams.Mode, theParams.Blocking, theParams.Compacting); GC.WaitForPendingFinalizers(); //GC.Collect(); // may need to collect dead objects created by the finalizers var elapsed = sw.ElapsedMilliseconds; long memAfter = GC.GetTotalMemory(true); Log.Info($"GC starts with {memBefore} bytes, ends with {memAfter} bytes, GC time {elapsed} (ms)"); } } // https://msdn.microsoft.com/en-us/library/system.runtime.gcsettings.largeobjectheapcompactionmode.aspx public static RunCompactingGC() { lock (CompactingGCLock) { var sw = Stopwatch.StartNew(); var timestamp = DateTime.Now; long memBefore = GC.GetTotalMemory(false); GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); var elapsed = sw.ElapsedMilliseconds; long memAfter = GC.GetTotalMemory(true); Log.Info($"Compacting GC starts with {memBefore} bytes, ends with {memAfter} bytes, GC time {elapsed} (ms)"); } }

Espero que esto ayude a alguien más, ya que pasamos mucho tiempo investigando esto.


Su punto número 3 es técnicamente correcto, pero solo puede suceder si alguien se bloquea durante un finalizador.

Incluso sin este tipo de llamada, bloquear dentro de un finalizador es incluso peor que lo que tiene aquí.

Hay un puñado de veces cuando llamar a GC.Collect() realmente ayuda al rendimiento.

Hasta ahora lo he hecho 2, tal vez 3 veces en mi carrera. (O quizás unas 5 o 6 veces si incluyes aquellas en las que lo hice, medí los resultados y luego lo sacaste de nuevo, y esto es algo que siempre debes medir después de hacerlo).

En los casos en los que se agita cientos o miles de megas de memoria en un corto período de tiempo, y luego se cambia a un uso mucho menos intensivo de la memoria durante un largo período de tiempo, puede ser una mejora masiva o incluso vital para Recoger explícitamente. ¿Eso es lo que está pasando aquí?

En cualquier otro lugar, en el mejor de los casos, lo harán más lento y usarán más memoria.


Vea mi otra respuesta aquí:

Para GC.Collect o no?

dos cosas pueden suceder cuando te llamas GC.Collect (): terminas gastando más tiempo haciendo colecciones (porque las colecciones de fondo normales seguirán sucediendo además de tu manual GC.Collect ()) y te quedarás en el Memoria más larga (porque forzó algunas cosas en una generación de orden superior que no necesitaba ir allí). En otras palabras, usar GC.Collect () por sí mismo es casi siempre una mala idea.

Casi la única vez que desea llamar a GC.Collect () es cuando tiene información específica sobre su programa que es difícil de saber para el recolector de basura. El ejemplo canónico es un programa de larga duración con distintos ciclos de carga ocupada y ligera. Es posible que desee forzar una colección cerca del final de un período de poca carga, antes de un ciclo de ocupado, para asegurarse de que los recursos sean lo más libres posible para el ciclo de ocupado. Pero incluso aquí, es posible que lo haga mejor si piensa de nuevo cómo se crea su aplicación (es decir, ¿funcionaría mejor una tarea programada?).