Не удается найти утечку памяти

Я работал над приложением WP7, приложением галереи изображений, с базовыми масштабированными масштабами и реализованными жестами.

В целях тестирования я скомпилировал приложение с автономными изображениями (их имена файлов пронумерованы), установленными в Content, и получил к ним доступ через жестко закодированную строку (которая будет заменена позже).

Но пришел к выводу, что приложение потребляет много памяти. Я думал, что это из-за изображений и нашел этот блог; изображения всегда кешируются. Я использовал код из блога, чтобы исправить это. Тем не менее память не освобождается, хотя скорость потребления действительно снизилась.

Для последней попытки я создал другое тестовое приложение с кнопкой базовой функции 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 МБ памяти, как только оно полностью показывает первую страницу с изображением, а после первого изменения страницы оно увеличивается почти до 18-20 МБ.
  • Последующее изменение страницы приводит к постепенному увеличению памяти, а затем возможному падению, если позволяет количество изображений, в противном случае после циклического перебора всех изображений потребление памяти остается постоянным
  • Я использую файлы.jpg размером примерно 1280 x 2000, для тестирования я не изменяю размеры изображений.

Сводка кучи -> Новые распределения Новые распределения">

4 ответа

Решение

У меня такое же приложение с кнопками перехода к следующему / предыдущему изображению. И у меня была точно такая же утечка памяти, которая сводила меня с ума.

Я до сих пор не смог найти основную причину, но мне удалось обойти это с помощью уродливого взлома. При отображении следующего изображения я заставляю старый источник изображения загружать недопустимое изображение, освобождая память. Я не понимаю, почему удаления всех ссылок и вызова сборщика мусора недостаточно, должна быть где-то другая внутренняя ссылка.

Во всяком случае, вот взломать:

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();
}

В некотором роде стыдно этим пользоваться, но, по крайней мере, это похоже на работу.

Я попробовал ваш пример кода, но в среде Windows Phone 8 я не смог воспроизвести утечку. Разница лишь в том, что я использовал свои собственные изображения.

Текущее использование памяти для моего 512 WVGA-эмулятора оставалось на уровне 13 МБ, а пик достиг 14 МБ. Я нажал кнопку "Далее" около 20 раз.

Также вы пытались использовать привязки для ImageHolder вместо установки источника вручную?

(кстати, визуально я не вижу никаких возможных утечек памяти в вашем коде).

(Также проверьте эту статью http://blogs.windows.com/windows_phone/b/wpdev/archive/2012/02/01/memory-profiling-for-application-performance.aspx)

После многих пробных запусков и отладочных сессий я обнаружил, что это кэширование изображений не выполняется (или не выполняется так агрессивно), когда изображения находятся в изолированном хранилище приложения.

Дело в том, что я использовал изображения, которые были частью файла xap, включенного в качестве содержимого. Я сделал это, потому что я просто хотел проверить мой просмотрщик изображений. Но это не тот случай, когда мое приложение будет готово. Приложение действительно было разработано для хранения изображений в изолированном хранилище и их отображения.

Поэтому я установил необходимый код и вуаля: теперь образы собираются мусором, хотя они все еще кэшируются. Смотрите изображение ниже (посмотрите, сколько раз вызывается сборщик мусора). Это решение не столь тривиального вопроса, поэтому никто другой не сталкивался с подобной проблемой.

Я полагаю, что когда WP7 silverlight обнаруживает, что изображения не из изолированного хранилища, он предполагает, что изображение из какого-то удаленного URI, и в любом случае решает его кэшировать. И вот тут-то и возникает проблема с кэшированием Silverlight. Другой ответ подтверждает, что в WP8 этого не происходит.

Попробуйте этот подход: загрузчик изображений с автоматической очисткой памяти. Это расширенный образец KooKiZ, который поддерживает визуализацию. Пример проекта находится здесь: https://simca.codeplex.com/

Другие вопросы по тегам