studio memoria guardar datos c# wpf memory-leaks

c# - guardar - Una muestra simple de WPF causa un crecimiento descontrolado de la memoria



guardar datos en android studio (4)

Normalmente en .NET GC se activa en la asignación de objetos al cruzar un cierto umbral, no depende de las bombas de mensajes (no me puedo imaginar que sea diferente con WPF).

Sospecho que los objetos Canvas están de alguna manera enraizados en el fondo o algo así. Si lo haces c.Children.Clear () justo antes de que termine el método BuildCanvas, el crecimiento de la memoria se ralentiza drásticamente.

De todos modos, como se comenta aquí, tal uso de elementos del marco es bastante inusual. ¿Por qué necesitas tantos lienzos?

He reducido un problema que estoy viendo en una de mis aplicaciones a una muestra de reproducción increíblemente simple. Necesito saber si hay algo extraño o algo que me falta.

De todos modos, abajo está el código. El comportamiento es que el código se ejecuta y crece constantemente en la memoria hasta que se bloquea con una OutOfMemoryException. Eso lleva un tiempo, pero el comportamiento es que los objetos se están asignando y no se están recogiendo basura.

He tomado basureros de memoria y he ejecutado! Gcroot en algunas cosas, así como HORMIGAS usadas para descubrir cuál es el problema, pero he estado en ello por un tiempo y necesito algunos ojos nuevos.

Esta muestra de reproducción es una aplicación de consola simple que crea un lienzo y le agrega una línea. Lo hace continuamente Esto es todo lo que hace el código. Duerme de vez en cuando para garantizar que la CPU no tenga tantos impuestos que el sistema no responda (y para garantizar que no haya problemas con el GC que no puede funcionar).

Alguien tiene alguna idea? Lo intenté con .NET 3.0 solamente, .NET 3.5 y también .NET 3.5 SP1 y el mismo comportamiento ocurrió en los tres entornos.

También tenga en cuenta que también puse este código en un proyecto de aplicación WPF y activé el código con un clic en el botón y allí también aparece.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Controls; using System.Windows.Shapes; using System.Windows; namespace SimplestReproSample { class Program { [STAThread] static void Main(string[] args) { long count = 0; while (true) { if (count++ % 100 == 0) { // sleep for a while to ensure we aren''t using up the whole CPU System.Threading.Thread.Sleep(50); } BuildCanvas(); } } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static void BuildCanvas() { Canvas c = new Canvas(); Line line = new Line(); line.X1 = 1; line.Y1 = 1; line.X2 = 100; line.Y2 = 100; line.Width = 100; c.Children.Add(line); c.Measure(new Size(300, 300)); c.Arrange(new Rect(0, 0, 300, 300)); } } }

NOTA: la primera respuesta a continuación es un poco fuera de base ya que explícitamente ya dije que este mismo comportamiento ocurre durante un evento de clic de botón de la aplicación WPF. Sin embargo, no dije explícitamente que en esa aplicación solo hago un número limitado de iteraciones (digamos 1000). Hacerlo de esa manera permitiría que el GC se ejecute mientras hace clic en la aplicación. También tenga en cuenta que dije explícitamente que tomé un volcado de memoria y descubrí que mis objetos estaban rooteados mediante! Gcroot. Tampoco estoy de acuerdo con que el GC no pueda funcionar. El GC no se ejecuta en el hilo principal de la aplicación de mi consola, especialmente porque estoy en una máquina de doble núcleo, lo que significa que el GC de estación de trabajo concurrente está activo. Bomba de mensaje, sin embargo, sí.

Para probar el punto, aquí hay una versión de la aplicación WPF que ejecuta la prueba en un DispatcherTimer. Realiza 1000 iteraciones durante un intervalo de temporizador de 100 ms. Tiempo más que suficiente para procesar cualquier mensaje de la bomba y mantener bajo el uso de la CPU.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; namespace SimpleReproSampleWpfApp { public partial class Window1 : Window { private System.Windows.Threading.DispatcherTimer _timer; public Window1() { InitializeComponent(); _timer = new System.Windows.Threading.DispatcherTimer(); _timer.Interval = TimeSpan.FromMilliseconds(100); _timer.Tick += new EventHandler(_timer_Tick); _timer.Start(); } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] void RunTest() { for (int i = 0; i < 1000; i++) { BuildCanvas(); } } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static void BuildCanvas() { Canvas c = new Canvas(); Line line = new Line(); line.X1 = 1; line.Y1 = 1; line.X2 = 100; line.Y2 = 100; line.Width = 100; c.Children.Add(line); c.Measure(new Size(300, 300)); c.Arrange(new Rect(0, 0, 300, 300)); } void _timer_Tick(object sender, EventArgs e) { _timer.Stop(); RunTest(); _timer.Start(); } } }

NOTA 2: utilicé el código de la primera respuesta y mi memoria creció muy lentamente. Tenga en cuenta que 1ms es mucho más lento y menos iteraciones que mi ejemplo. Tienes que dejarlo correr por un par de minutos antes de que comiences a notar crecimiento. Después de 5 minutos, está a 46 MB desde un punto de partida de 30 MB.

NOTA 3: Eliminar la llamada a. Organizar elimina por completo el crecimiento. Desafortunadamente, esa llamada es bastante vital para mi uso, ya que en muchos casos estoy creando archivos PNG desde el Canvas (a través de la clase RenderTargetBitmap). Sin la llamada a. Arregle no distribuye el lienzo en absoluto.


Pude reproducir su problema utilizando el código que proporcionó. La memoria sigue creciendo porque los objetos Canvas nunca se liberan; un perfilador de memoria indica que el ContextLayoutManager del Dispatcher los está reteniendo a todos (para que pueda invocar OnRenderSizeChanged cuando sea necesario).

Parece que una solución simple es agregar

c.UpdateLayout()

hasta el final de BuildCanvas .

Dicho esto, tenga en cuenta que Canvas es un UIElement ; se supone que debe usarse en UI. No está diseñado para ser utilizado como una superficie de dibujo arbitraria. Como ya comentaron otros comentaristas, la creación de miles de objetos Canvas puede indicar un defecto de diseño. Me doy cuenta de que su código de producción puede ser más complicado, pero si solo dibuja formas simples en un lienzo, el código basado en GDI + (es decir, las clases System.Drawing) puede ser más apropiado.


Editar 2: Obviamente no es la respuesta, pero fue parte del intercambio de respuestas y comentarios aquí, así que no lo borro.

El GC nunca tiene la oportunidad de recolectar esos objetos porque su ciclo y sus llamadas de bloqueo nunca terminan, y por lo tanto, el mensaje bomba y los eventos nunca llegan a su turno. Si usó un Timer de algún tipo para que los mensajes y eventos realmente tengan la oportunidad de procesarse, probablemente no pueda consumir toda su memoria.

Editar: Lo siguiente no me consume la memoria siempre que el intervalo sea mayor que cero. Incluso si el intervalo es de solo 1 Tick, siempre que no sea 0. Si es 0, volvemos al ciclo infinito.

public partial class Window1 : Window { Class1 c; DispatcherTimer t; int count = 0; public Window1() { InitializeComponent(); t = new DispatcherTimer(); t.Interval = TimeSpan.FromMilliseconds( 1 ); t.Tick += new EventHandler( t_Tick ); t.Start(); } void t_Tick( object sender, EventArgs e ) { count++; BuildCanvas(); } private static void BuildCanvas() { Canvas c = new Canvas(); Line line = new Line(); line.X1 = 1; line.Y1 = 1; line.X2 = 100; line.Y2 = 100; line.Width = 100; c.Children.Add( line ); c.Measure( new Size( 300, 300 ) ); c.Arrange( new Rect( 0, 0, 300, 300 ) ); } }


WPF en .NET 3 y 3.5 tiene una pérdida de memoria interna. Solo se dispara bajo ciertas situaciones. Nunca pudimos averiguar exactamente qué lo desencadena, pero lo teníamos en nuestra aplicación. Aparentemente está arreglado en .NET 4.

Creo que es el mismo que se menciona en esta publicación de blog

En cualquier caso, poner el siguiente código en el constructor App.xaml.cs resolvió para nosotros

public partial class App : Application { public App() { new HwndSource(new HwndSourceParameters()); } }

Si nada más lo resuelve, intente eso y vea