Не удается найти утечку памяти
Я работал над приложением 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/