1

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

4

3 回答 3

1

内存泄漏不在 ImageTools 中,但实际上是 Silverlight Runtime 中的错误:

动态添加和删除图像时的内存泄漏

解决方法:从应用程序中动态添加或删除 BitmapImages(也就是从树中添加/删除)时,您应该在从树中删除 Image 元素之前设置 Image.Source = null。这将使 BitmapImage 有资格进行垃圾收集。错误状态:活动错误。*

参考:

Silverlight:如何从内存中卸载(处置)图像? 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)
于 2016-06-17T23:27:00.370 回答
0

它应该在应用程序页面导航离开时收集

将代码放入导航过程中以实际调用 dispose 并取消图像与页面的链接。任何持有图像的东西都可能不会被处理,因此图像永远不会被处理。用手做,然后看看是否发生了同样的事情。


OP 报告说有关动画图像的问题仍然是一个问题。确保图像没有订阅pinning内存。必须通过取消-=订阅过程删除所有订阅。否则,即使没有对该对象的引用,任何订阅都会将其固定到内存中以使其保持活动状态。

于 2013-09-11T17:55:47.900 回答
-1

如果有人偶然发现这...

请参阅此处以获取解决方案。

简短的回答:在离开之前在 AnimatedImage 上调用 Stop()。

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

于 2013-11-21T01:02:59.320 回答