我有一个WindowsPhone 7.8应用程序,需要显示动画GIF以及Silverlight直接支持的图像。因此,我们创建了一个自定义用户控件public sealed partial class ExtendedImageControl : UserControl, IDisposable
,它有一个名为ImageSource的DependancyProperty。此属性绑定到URL。然后,后面的代码将常规Image控件插入LayoutRoot或AnimatedImage。但是,AnimatedImage在离开视图或关闭包含页面时不会释放其内存。
加载逻辑如下:
ExtendedImage loadedImage = (ExtendedImage)await Utils.Caching.Image.LoadCachedImageFromUrlAsync<ExtendedImage>(loadedLocation);
if (ImageSource == loadedLocation)
{
AnimatedImage image = new AnimatedImage();
image.Stretch = stretch;
image.Source = loadedImage;
LayoutRoot.Children.Add(image);
CurrentImageMode = ExtendedImageMode.AnimatedImage;
loadedImage = null;
#if DEBUG
App.logger.log("Loaded {0} as animated image", loadedLocation);
#endif
imageDisplay = image;
raiseImageUpdated();
}
最终,图像加载
WebClient client = new WebClient();
ExtendedImage image = new ExtendedImage();
using (Stream source = await client.OpenReadTaskAsync(location))
{
if (location.ToString().EndsWith("gif", StringComparison.InvariantCultureIgnoreCase))
{
image.SetSource(source);
TaskCompletionSource<ExtendedImage> imageLoaded = new TaskCompletionSource<ExtendedImage>();
EventHandler loadingCompleteHandler = new EventHandler((sender, e) =>
{
imageLoaded.SetResult(image);
});
EventHandler<UnhandledExceptionEventArgs> loadingFailedHandler = new EventHandler<UnhandledExceptionEventArgs>((sender, e) =>
{
imageLoaded.SetResult(image);
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Break();
#endif
});
image.LoadingCompleted += loadingCompleteHandler;
image.LoadingFailed += loadingFailedHandler;
image = await imageLoaded.Task;
//Remove handlers, otherwise the object might be kept in the memory
image.LoadingCompleted -= loadingCompleteHandler;
image.LoadingFailed -= loadingFailedHandler;
}
else
{
//... load with native Silverlight methods
}
}
return image;
我们很早就注意到了内存问题,因此控件使用
实现了IDisposable接口 public void Dispose()
{
unloadImage();
GC.SuppressFinalize(this);
}
private void unloadImage()
{
SmartDispatcher.BeginInvoke(() =>
{
if (imageDisplay != null)
{
if (imageDisplay is AnimatedImage)
{
if ((imageDisplay as AnimatedImage).Source != null & (imageDisplay as AnimatedImage).Source.Frames != null)
(imageDisplay as AnimatedImage).Source.Frames.Clear();
(imageDisplay as AnimatedImage).Stop();
(imageDisplay as AnimatedImage).Source = null;
}
else if (imageDisplay is Image && ((Image)imageDisplay).Source != GIFplaceholder)
{
(imageDisplay as Image).Source = null;
}
imageDisplay = null;
}
});
}
但是,从不在图像上调用Dispose方法。
我该怎么做才能找出为什么GC不会选择这个对象?我没有注册任何事件处理程序,据我所知,应该在导航应用程序页面时收集它。我也尝试添加一个析构函数,但是这个也没有被调用。
答案 0 :(得分:1)
当应用程序页面离开
时应该收集它
将代码放入导航过程中以实际调用dispose并取消链接页面中的图像。无论如何保持图像都不会被丢弃,因此图像永远不会被丢弃。用手做,然后看看是否发生了同样的情况。
OP报道说有关动画图像的问题仍然存在。确保没有订阅pinning
图像到内存。必须使用-=
取消订阅流程删除所有订阅。否则,即使没有对该对象的引用,任何订阅引脚都会将其保存到内存中。
答案 1 :(得分:1)
内存泄漏不在ImageTools中,但实际上是Silverlight Runtime中的一个错误:
动态添加和删除图像时内存泄漏
解决方法:从动态添加或删除BitmapImages时 应用程序(例如,从树中添加/删除),您应该设置 从树中删除Image元素之前,Image.Source = null。 这将使BitmapImage符合垃圾回收的条件。窃听器 状态:活动错误。 *
参考文献:
Silverlight: How to unload (dispose) an image from memory? https://blogs.msdn.microsoft.com/silverlight_sdk/2008/10/28/silverlight-bugs-and-workarounds/
可以快速验证在返回XAML页面之前将每个ExtendedImage设置为Nothing可以显着减少应用程序内存的使用。
我将以下内容添加到App.xaml中的RootFrame_Navigating事件中,以帮助跟踪这些泄漏:
Dim applicationCurrentMemoryUsage As Long = Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage")
Dim applicationPeakMemoryUsage As Long = Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage")
Debug.WriteLine(DateTime.Now.ToLongTimeString() & " Current : " & applicationCurrentMemoryUsage & " Peak : " & applicationPeakMemoryUsage)
答案 2 :(得分:-1)
如果有人绊倒了......
请参阅here了解解决方案。
简短回答:在导航之前调用AnimatedImage上的Stop()。
编辑:我已经更新了库,以便在从页面中删除控件时自动停止动画。下载并构建最新版本的源代码。