Fuga de memoria WPF

Tengo una aplicación wpf simple. En la ventana principal tengo panel de stack y 2 botones. El primer botón agrega 100 controles de usuario (sin ningún enlace de datos, eventos, mapas de bits) y el segundo los elimina del panel y llama a GC.Collect (). Y hay algunos problemas: 1. Después de hacer clic en el botón “quitar” por primera vez, no se libera toda la memoria, y debo hacer clic varias veces para liberar más memoria. 2. Después de 5 a 10 minutos, la memoria se libera pero pocos megabytes no.

por ejemplo, después de que mi aplicación se inicie, se requieren ~ 22 mb cuando se agregan 500 controles – ~ 60 mb después de hacer clic en el botón “quitar” por primera vez – ~ 55 mb (espero un tiempo, la memoria no se desasigna) hago clic varias veces y la memoria se redujo a 25 mb , No entiendo esto, soy nuevo en WPF, y tal vez extraño algo que quiero liberar memoria inmediatamente.

             

 namespace WpfApplication10 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void button1_Click(object sender, RoutedEventArgs e) { int N = 100; //var r = new ActivityStatisticItem("111", "222", DateTime.Now, "333", 1); for (int i = 0; i < N; i++) { activityStackPanel.Children.Add(new UserControl1()); } label1.Content = activityStackPanel.Children.Count; } private void button2_Click(object sender, RoutedEventArgs e) { activityStackPanel.Children.Clear(); label1.Content = activityStackPanel.Children.Count; GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } } }          

En control de usuario solo tengo

  public UserControl1() { InitializeComponent(); } 

Quiero liberar la memoria de inmediato.

No lo hagas Confíe en GC.

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

No lo hagas Confíe en GC.

Después de 5 – 10 min lanzamientos de memoria

¿No te digo confianza GC?


  • El modelo de recolección de basura se asegurará de que se libere la memoria administrada no deseada en su sistema (que incluye casi la totalidad de la memoria de sus controles). Utiliza un algoritmo para la optimización que incluye generaciones, memoria libre disponible, posiblemente CPU disponible … para que GC.Collect() interfiera con él.

  • GC.Collect() es asíncrono, por lo que no tiene un efecto inmediato.

  • El único recurso que debe tener cuidado es el recurso no administrado que generalmente se maneja con Dispose Pattern . De lo contrario no te metas con GC, hace su trabajo muy bien.

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

Esta es una forma segura de forzar a los objetos que no se pueden GC en Gen2 prematuramente, lo que aumenta su huella de memoria por un período de tiempo más largo, sin ninguna buena razón.

Como dijo Aliostad: ¡no!

Deja el recolector de basura solo y deja que haga su trabajo.

Lo que estás describiendo no es una pérdida de memoria. La memoria dinámica no se libera en el momento en que crees que debería publicarse.

¿Eres el recolector de basura? Usted no. Preocuparse por cuándo se recolecta la basura no es su trabajo. Si estos objetos son en realidad basura, y lo son, la memoria estará allí cuando la necesite.

En un entorno de recolección de basura, liberar memoria de inmediato no tiene sentido.

Dado que el código CLR JITs a pedido, la primera vez que ejecuta su prueba, no debería ver que la memoria vuelve a su lugar inicial. Esto tiene sentido porque se han seguido nuevas rutas de código y el código ha sido JITted. Ese código necesita residir en algún lugar en la memoria ¿no?

Por lo tanto, después de su primera ejecución de prueba, no debería poder recuperar su huella de memoria inicial. Su línea de base debe ser el uso de memoria que obtiene después de ejecutar la prueba una vez, no antes. Después de ejecutar una segunda vez, puedo recuperar la memoria hasta la línea de base con varias colecciones.

Además, recomiendo ejecutar su proyecto en modo de lanzamiento sin un depurador adjunto. Ejecutar su progtwig con un depurador adjunto no le mostrará un verdadero perfil de memoria, ya que hay varios trucos que emplea para mantener los objetos a su alrededor (por ejemplo, recostackr objetos todavía en su scope – GC.Collect ).

Sin embargo, todo esto es un punto discutible, porque como dije anteriormente, recuperar la memoria de inmediato no tiene mucho sentido en un entorno de GC (en la mayoría de los casos).

Estoy de acuerdo con @Aliostad re GC. Hace su trabajo muy bien, PERO no es una herramienta para limpiar mágicamente toda tu memoria.

Si tiene problemas de pérdida de memoria, la solución más sencilla y confiable es utilizar un generador de perfiles, que debería poder identificar si tiene una pérdida real y dónde está. He usado las ants de Red Gate, pero otras pueden tener mejores sugerencias.

Además de seguir las pautas habituales, como asegurarse de disponer adecuadamente de las cosas. Llamar a GC y esperar que funcione no es una alternativa para la evaluación correcta del código.

Al usar este Dll Invoke podemos reasignar los recursos de memoria.

 public class MemoryManagement { [DllImportAttribute("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)] private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize); public static void FlushMemory() { GC.Collect(); GC.WaitForPendingFinalizers(); if (Environment.OSVersion.Platform == PlatformID.Win32NT) { SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1); } }