ImageTools AnimatedImage不释放内存

时间:2013-09-09 12:29:38

标签: c# .net silverlight windows-phone-7

我有一个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不会选择这个对象?我没有注册任何事件处理程序,据我所知,应该在导航应用程序页面时收集它。我也尝试添加一个析构函数,但是这个也没有被调用。

3 个答案:

答案 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()。

编辑:我已经更新了库,以便在从页面中删除控件时自动停止动画。下载并构建最新版本的源代码。