c# - No se puede encontrar la pérdida de memoria
image windows-phone-7 (4)
He intentado con tu ejemplo de código, pero en el entorno de Windows Phone 8 no pude reproducir la fuga. La única diferencia es que he usado mis propias imágenes.
El uso de memoria actual se mantuvo en 13 MB para mi 512 WVGA Emulator y el Peak se mantuvo en 14 MB. He pulsado el "próximo botón" alrededor de 20 veces.
¿También ha intentado utilizar enlaces para ImageHolder en lugar de configurar el origen manualmente?
(Por cierto, visualmente no veo ninguna posible pérdida de memoria en su código).
(También consulte este artículo http://blogs.windows.com/windows_phone/b/wpdev/archive/2012/02/01/memory-profiling-for-application-performance.aspx )
He estado trabajando en una aplicación WP7, es una aplicación de galería de imágenes, con zoom básico y gestos de flick implementados.
Para fines de prueba, compilé la aplicación con imágenes sin conexión (sus nombres de archivo están numerados) configurados en Contenido y accedí a ellos a través de una cadena codificada (que se reemplazará más adelante).
Pero me di cuenta de que la aplicación consume mucha memoria. Pensé que se debía a imágenes y encontré este blog ; las imágenes siempre estaban en el caché. Usé el código del blog para rectificar esto. Todavía no se libera memoria, aunque la tasa de consumo disminuyó.
Para el intento final, creé otra aplicación de prueba con el botón de función básica 2 para la navegación y el control de imágenes para las imágenes, solo para asegurarme de que no fueran mis códigos de gestos los que podrían ser el problema.
Este es el xaml
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0" x:Name="ImageHolder" Height="Auto" Width="Auto" Stretch="Uniform" Tap="image_Tap" />
<TextBlock x:Name="MemUsage" />
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button x:Name="PrevButton" Content="Prev" Width="240" Click="btnPrev_Click"/>
<Button x:Name="NextButton" Content="Next" Width="240" Click="btnNext_Click"/>
</StackPanel>
</Grid>
Este es el archivo .cs
const int PAGE_COUNT = 42;
int pageNum = 0;
public MainPage()
{
InitializeComponent();
RefreshImage();
}
private void btnPrev_Click(object sender, RoutedEventArgs e)
{
pageNum = (PAGE_COUNT + pageNum - 1) % PAGE_COUNT; // cycle to prev image
RefreshImage();
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
pageNum = (PAGE_COUNT + pageNum + 1) % PAGE_COUNT; // cycle to next image
RefreshImage();
}
private void image_Tap(object sender, GestureEventArgs e)
{
RefreshTextData();
}
private void RefreshImage()
{
BitmapImage image = ImageHolder.Source as BitmapImage;
ImageHolder.Source = null;
if (image != null)
{
image.UriSource = null;
image = null;
}
ImageHolder.Source = new BitmapImage(new Uri("000//image" + (pageNum + 1).ToString("D3") + ".jpg", UriKind.Relative));
RefreshTextData();
}
private void RefreshTextData()
{
MemUsage.Text = "Device Total Memory = " + (long)DeviceExtendedProperties.GetValue("DeviceTotalMemory") / (1024 * 1024)
+ "/nCurrent Memory Usage = " + (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage") / (1024 * 1024)
+ "/nPeak Memory Usage = " + (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage") / (1024 * 1024);
}
Pero aún hay pérdida de memoria y no puedo precisarlo. Estoy teniendo dificultades para encontrarlo. El perfilador de memoria muestra que tengo muchas instancias de una cadena, y no puedo interpretar eso.
Pocos puntos:
- Tengo imágenes en una carpeta "000" y el nombre "imagen ###". En este momento tengo imágenes con nombres de archivo de "image001" a "image042"
- La aplicación de prueba tiene una huella de memoria de 6 MB tan pronto como muestra la primera página completamente con la imagen, y después de un cambio de página exacto sube a casi 18-20 MB
- El cambio de página posterior da como resultado un aumento gradual en la memoria y luego un bloqueo eventual, si el número de imágenes lo permite; de lo contrario, después de recorrer todas las imágenes, el consumo de memoria es constante.
- Estoy usando archivos .jpg con una dimensión aproximada de 1280 x 2000, para probar no estoy redimensionando las imágenes.
Después de muchas ejecuciones de prueba y depuración encontré que este almacenamiento en caché de imágenes no se realiza (o no se realiza de forma tan agresiva) cuando las imágenes residen en IsolatedStorage de la aplicación.
La cosa es que estaba usando las imágenes que formaban parte del archivo xap, incluidas como contenido. Hice esto porque solo quería probar mi visor de imágenes. Pero este no sería el caso cuando mi aplicación termine. La aplicación fue diseñada para almacenar imágenes en almacenamiento aislado y mostrarlas.
Así que configuré el código necesario y listo, ahora las imágenes están siendo recolectadas, aunque todavía estén en la memoria caché. Vea la imagen a continuación (vea cuántas veces se llama a Garbage Collector). Esta es una solución a una pregunta no tan trivial, es por eso que nadie más se enfrentó a un problema de este tipo.
Creo que cuando WP7 silverlight descubre que las imágenes no provienen del almacenamiento aislado, asume que la imagen proviene de algún URI remoto y decide almacenarla de todos modos. Y ahí es donde entra en juego el problema del almacenamiento en caché de la imagen Silverlight. Otra respuesta confirma que esto no ocurre en WP8.
Pruebe este enfoque: Descarga de imágenes con limpieza automática de la memoria . Se trata de una muestra avanzada de KooKiZ, que admite la visualización. El proyecto de muestra está aquí: https://simca.codeplex.com/
Tengo el mismo tipo de aplicación, con los botones de imagen siguiente / anterior. Y tuve exactamente la misma pérdida de memoria, lo que me ha vuelto loco.
Todavía no he podido encontrar la causa raíz, pero me las arreglé para eludirlo con un hack feo. Cuando visualice la siguiente imagen, obligo a la fuente de imagen anterior a cargar una imagen no válida, liberando así la memoria. No entiendo por qué no es suficiente eliminar todas las referencias y llamar al recolector de basura, debe haber otra referencia guardada internamente en alguna parte.
De todos modos, aquí está el truco:
private void DisposeImage(BitmapImage image)
{
if (image != null)
{
try
{
using (var ms = new MemoryStream(new byte[] { 0x0 }))
{
image.SetSource(ms);
}
}
catch (Exception)
{
}
}
}
Puede llamarlo, por ejemplo, en su método RefreshImage
:
private void RefreshImage()
{
BitmapImage image = ImageHolder.Source as BitmapImage;
ImageHolder.Source = null;
DisposeImage(image);
ImageHolder.Source = new BitmapImage(new Uri("000//image" + (pageNum + 1).ToString("D3") + ".jpg", UriKind.Relative));
RefreshTextData();
}
Un poco avergonzado de usar eso, pero al menos parece funcionar.