找不到内存泄漏问题

9
我一直在开发一个WP7应用,它是一个图库应用程序,基本的缩放和滑动手势已经实现。
为了测试目的,我将应用程序编译为离线图像(它们的文件名是有编号的),并通过硬编码字符串来访问它们(稍后将被替换)。
但是我发现应用程序消耗了很多内存。我以为这是由于图片,找到了这篇博客,图片一直在缓存。我使用了博客中的代码来纠正这个问题。尽管消耗速度有所降低,但仍然没有释放内存。
为了最后的尝试,我创建了另一个测试应用程序,它具有基本功能:2个用于导航的按钮和控制图像的图像控件,只是为了确保它不是我的手势代码可能出现的问题。
这是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>

这是一个 .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);
    }

但是仍然存在内存泄漏,我无法确定问题出在哪里。我很难找到它。内存分析器显示我有许多字符串实例,我无法理解。

几个要点:

  • 我有一个名为“000”的文件夹中的图像,并以“image###”命名。目前,我的图像文件名从“image001”到“image042”
  • 测试应用程序在完全显示第一页和图像后立即具有6 MB的内存占用量,而在第一页更改后,它会增加到将近18-20 MB
  • 随后的页面更改会导致内存逐渐增加,然后最终崩溃(如果图像数量允许),否则在循环所有图像后,内存消耗保持不变
  • 我正在使用大约尺寸为1280 x 2000的 .jpg 文件进行测试,对于测试,我没有调整图像大小。

Heap Summary -> New Allocations


你是否为每个显示的图像产生了一个额外的线程来渲染它?你周围有1918个线程对象。这听起来不健康。找出如何创建这些线程并正确终止它们,以便可以释放线程方法引用的相关资源。 - Alois Kraus
@Alois,我不是专业人士,只是一个爱好者开发者。我真的不知道我在哪里不小心创建线程。我没有显式地创建任何线程。我甚至不知道这是否真的是个问题。我能说的是,你看到的是我的示例应用程序的全部代码。如果你可以重新创建它并查看一下,可能会有帮助。 - Master Chief
4个回答

6
我有一个类似的应用程序,带有下一个/上一个图片按钮。我也遇到了同样的内存泄漏问题,这让我很烦恼。
尽管我还没有找到根本原因,但我成功地通过一种丑陋的方法绕过了它。当显示下一张图片时,我强制旧图像源加载一个无效的图片,从而释放内存。我不明白为什么删除所有引用并调用垃圾收集器不足以解决问题,可能在某个内部位置保留了另一个引用。
无论如何,以下是该方法:
private void DisposeImage(BitmapImage image)
{
    if (image != null)
    {
        try
        {
            using (var ms = new MemoryStream(new byte[] { 0x0 }))
            {
                image.SetSource(ms);
            }
        }
        catch (Exception)
        {
        }
    }
}

您可以在您的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();
}

有点不好意思使用它,但至少它似乎能够工作。


我一直在找你。我认为你是WP7领域中最活跃的成员之一。Stack Overflow可以开发一种PM服务。不管怎样,你提出的解决方案似乎是正确的。我自己也想过这个方法。我会将它标记为答案,但我不会确认它。请看我的答案。但还是谢谢你的帮助。 - Master Chief

1
我已经尝试了你的代码示例,但在Windows Phone 8环境下,我无法复制泄漏。唯一的区别是我使用了自己的图像。
我的512 WVGA模拟器的当前内存使用量保持在13MB,峰值保持在14MB。我按了“下一个按钮”大约20次。
另外,你尝试过使用Bindings来代替手动设置ImageHolder的Source吗?
(顺便说一句,从视觉上看,我在你的代码后台没有发现任何可能的内存泄漏。)
(还要检查这篇文章http://blogs.windows.com/windows_phone/b/wpdev/archive/2012/02/01/memory-profiling-for-application-performance.aspx

从视觉上看,我在你的代码后端中没有发现任何可能的内存泄漏,这让我感到困惑。我会尝试数据绑定,谢谢回复。 - Master Chief

1
经过多次试运行和调试,我发现当图像驻留在应用程序的独立存储中时,这种图像缓存并没有执行(或者执行得不够积极)。
问题是,我使用的是作为内容包含在xap文件中的图像。我这样做只是因为我想测试我的图像查看器。但是当我的应用程序完成时,情况就不同了。应用程序真正设计的是将图像存储在独立存储中并显示它们。
所以我设置了必要的代码,然后,哇,即使它们仍然被缓存,图像现在也被垃圾回收了。请参见下面的图像(查看垃圾回收器被调用的次数)。这是一个不太平凡的问题的解决方案,这就是为什么没有其他人遇到这种问题的原因。
我相信,当WP7 Silverlight发现图像不来自独立存储时,它会认为图像来自某个远程URI,并决定无论如何都要对其进行缓存。这就是Silverlight图像缓存问题出现的地方。正如另一个答案所确认的那样,在WP8中不会发生这种情况。 enter image description here

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接