I have been working on a WP7 app, it's image gallery app, with basic zooming and flick gestures implemented.
For test purposes I compiled the app with offline images(their filenames are numbered) set to Content and accessed them via hard coded string (which will be replaced later).
But came to realize that app consumes a lot of memory. I thought it was due to images and found this blog; images were always caching. I used the code from the blog to rectify this. Still memory is not released, although rate of consumption did go down.
For final attempt I created another test app with basic feature 2 button for navigation and image control for images, just to make sure it was not my gesture codes that could be the problem.
This is the 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>
This is the .cs file
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);
}
But still memory leak is there and I can't pin point it. I am having a hard time finding it. Memory profiler shows that I have many instances of a string, and I can't interpret that.
Few Points:
I have the same kind of app, with the next/previous picture buttons. And I had exactly the same memory leak, which has driven me mad.
I still haven't been able to find the root cause, but I've managed to bypass it with a ugly hack. When displaying the next picture, I force the old image source to load an invalid picture, thus freeing the memory. I don't understand why removing all references and calling the garbage collector isn't enough, there must be another reference kept internally somewhere.
Anyway, here is the hack:
private void DisposeImage(BitmapImage image)
{
if (image != null)
{
try
{
using (var ms = new MemoryStream(new byte[] { 0x0 }))
{
image.SetSource(ms);
}
}
catch (Exception)
{
}
}
}
You can call it for instance in your RefreshImage
method:
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();
}
Kinda ashamed to use that, but at least it seems to work.
I have tried your code sample, but in Windows Phone 8 environment and I couldn't reproduce the leak. The only difference is that I've used my own images.
The current memory usage stayed at 13MB for my 512 WVGA Emulator and the Peak stayed at 14MB. I have pushed the "next button" around 20 times.
Also have you tried using Bindings for ImageHolder instead of setting the Source manually?
(btw, visually I don't see any possible memory leaks in your codebehind).
(Also check this article http://blogs.windows.com/windows_phone/b/wpdev/archive/2012/02/01/memory-profiling-for-application-performance.aspx )
After many trial runs and debugging sessions I found that this caching of images is not performed (or not performed as aggressively) when images are residing in the app's IsolatedStorage.
Thing is I was using the images which were a part of the xap file, included as content. I did this because I just wanted to test my image viewer. But this would not be the case when my app would finish. App was really designed to store images in Isolated storage and display them.
So I setup the necessary code and voila, Images are now getting garbage collected, even though they still got cached. See image below(see how many times Garbage Collector gets called). This is a solution to a not so trivial question, that's why no one else faced problem of this kind.
I believe that when WP7 silverlight finds that images is not from Isolated storage it assumes image is from some remote URI and decides to cache it anyway. And that's where silverlight image caching problem kicks in. As another answer confirms this doesn't happens in WP8.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With