c# - EscrituraBitmap memoria de fugas?
windows-phone writeablebitmap (5)
Estoy usando el siguiente código para crear un mosaico activo, basado en un elemento de la interfaz de usuario. uiElement
el elemento uiElement
en un WriteableBitmap
, guarda el mapa de bits + devuelve el nombre del archivo. Este método se ejecuta en un agente de tareas en segundo plano de Windows Phone y estoy ejecutando dentro de los límites de memoria.
private string CreateLiveTileImage(Canvas uiElement, int width, int heigth)
{
var wbmp = new WriteableBitmap(width, heigth);
try
{
wbmp.Render(uiElement, null);
wbmp.Invalidate();
var tileImageName = _liveTileStoreLocation;
using (var stream = new IsolatedStorageFileStream(tileImageName, FileMode.Create, FileAccess.Write, IsolatedStorageFile.GetUserStoreForApplication()))
{
wbmp.SaveJpeg(stream, width, heigth, 0, 100);
stream.Close();
}
uiElement = null;
wbmp = null;
GC.Collect();
return "isostore:" + tileImageName;
}
catch (Exception exception)
{
// ...
}
return null;
}
Hice algunas pruebas y el problema es: ¡Este método pierde memoria, pero no sé por qué / dónde?
También hice algunas ejecuciones de prueba, antes de ejecutar por primera vez este método:
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
7.249.920 Bytes
Esto está bien, ya que el depurador está conectado, que utiliza aproximadamente 2 MB de memoria.
Haciendo algunas ejecuciones más de este método (volver a ejecutar el método de ejecución en el depurador):
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
8851456 long + 40960
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
8892416 long + 245760
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9138176 long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9281536 long + 151552
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9433088 long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9576448 long + 139264
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9715712 long + 139264
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9859072 long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
10006528 long + 147456
Así aumenta la memoria, utilizada por este método.
¿Pero por qué? En mi opinión, no hay referencias que eviten que los objetos se recojan.
ACTUALIZACIÓN el 04.05.2013
Hola,
¡Gracias por todas tus respuestas! Como se sugirió, reduje el código + finalmente pude reproducir el problema en unas pocas líneas de código.
void Main()
{
for (int i = 0; i < 100; i++)
{
CreateImage();
}
}
private void CreateImage()
{
var rectangle = CreateRectangle();
var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);
rectangle = null;
writeableBitmap = null;
GC.Collect();
}
private Rectangle CreateRectangle()
{
var solidColorBrush = new SolidColorBrush(Colors.Blue);
var rectangle = new Rectangle
{
Width = 1000,
Height = 1000,
Fill = solidColorBrush // !!! THIS causes that the image writeableBitmap never gets garbage collected
};
return rectangle;
}
Después de iniciar la aplicación: ApplicationCurrentMemoryUsage: "11 681 792 Bytes"
1 Iteración - ApplicationCurrentMemoryUsage: "28 090 368 Bytes"
5 iteraciones - AplicaciónMemoria actualUso: "77 111 296 Bytes"
20 iteraciones - AplicaciónMemoria actualUso: "260 378 624 Bytes"
Después de 23 iteraciones: fuera de la excepción de memoria. Ln .: var writeableBitmap = new WriteableBitmap (rectangle, rectangle.RenderTransform);
Solo al comentar la línea "Fill = solidColorBrush", se llamó al método CreateImage () 100 veces sin ningún problema, después de la iteración número 100, el uso de memoria fue de "16 064 512 Bytes".
¡Así que parece que el problema es el cepillo! Cuando se usa para rellenar un elemento de la interfaz de usuario, y luego este elemento de la interfaz de usuario se representa en un mapa de bits de escritura, el mapa de bits nunca se recolecta como basura.
Por supuesto, esto no tiene sentido en mi opinión. El cepillo queda fuera del alcance por lo que simplemente debe ser recolectado también! (Establecer el pincel en nulo después de su uso no cambió nada)
Muchos de mis elementos de la interfaz de usuario utilizan un pincel para rellenar, por lo que no puedo simplemente eliminar el uso de los pinceles. ¿Qué piensas sobre este asunto?
El problema es que rectangle.RenderTransform es una instancia de un objeto y si configura writableBitmap en null the rectangle.RenderTransform El objeto aún está vivo y guarda el rectángulo en la memoria ... por lo que la solución es editar el código de la siguiente manera:
private void CreateImage()
{
var rectangle = CreateRectangle();
var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);
rectangle.RenderTransform = null; //and your memory would be happy ;)
rectangle = null;
writeableBitmap = null;
GC.Collect();
}
ver las capturas de pantalla de memoria ...
antes de:
después:
Intenta poner esta parte:
uiElement = null;
wbmp = null;
GC.Collect();
para finalmente la cláusula;
Obtengo la misma excepción cuando convierta uielement a writablebitmap en taskagent. Lo he intentado tantas veces. He encontrado una solución que se ha trabajado.
wbmp.SaveJpeg(stream, width, heigth, 0, 60);
puede cambiar la calidad a 60 cuando guarde uielement para transmitir. bien reduce el uso de memoria. puedes probarlo.
Puede limitar el tamaño del ancho X de la imagen del mapa de bits que se puede escribir a un tamaño más pequeño a medida que aumenta el tamaño y ocupa más memoria, por lo que puede limitar su tamaño inicial al principio y luego guardar como jpeg con el tamaño original del mosaico
Su finalmente en el catch catch debería ser wbmp = null para un saque inicial.
Y lo está representando, lo que significa que está adjuntando ese objeto a una lista externa (la función). Por lo tanto, ninguna cantidad de GC.Collect realmente lo recogerá ya que todavía está ''en juego''.
Establezca la fuente de datos en nulo, que puede eliminar la referencia adjunta por la que el GC la ignora.