Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
451 views
in Technique[技术] by (71.8m points)

c# - Cannot find the memory leak

I have been working on a WP7 app, it's image gallery app, with basic zooming and flick gestures implemented.

For test purposes I compiled the app with offline images(their filenames are numbered) set to Content and accessed them via hard coded string (which will be replaced later).

But came to realize that app consumes a lot of memory. I thought it was due to images and found this blog; images were always caching. I used the code from the blog to rectify this. Still memory is not released, although rate of consumption did go down.

For final attempt I created another test app with basic feature 2 button for navigation and image control for images, just to make sure it was not my gesture codes that could be the problem.

This is the 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>

This is the .cs file

    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)
            + "
Current Memory Usage = " + (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage") / (1024 * 1024)
            + "
Peak Memory Usage = " + (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage") / (1024 * 1024);
    }

But still memory leak is there and I can't pin point it. I am having a hard time finding it. Memory profiler shows that I have many instances of a string, and I can't interpret that.

Few Points:

  • I have images in a folder "000" and named "image###". At present I have images with file names from "image001" to "image042"
  • Test app has a memory footprint of 6 MB as soon as it shows the first page completely with the image, and after fisrt page change it rises to almost 18-20 MB
  • Subsequent page change result in gradual increase in memory and then eventual crash, if number of images permit, otherwise after cycling through all images memory consumption is constant
  • I am using .jpg files with approx dimension 1280 x 2000, for testing I am not resizing images.

Heap Summary -> New Allocations

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I have the same kind of app, with the next/previous picture buttons. And I had exactly the same memory leak, which has driven me mad.

I still haven't been able to find the root cause, but I've managed to bypass it with a ugly hack. When displaying the next picture, I force the old image source to load an invalid picture, thus freeing the memory. I don't understand why removing all references and calling the garbage collector isn't enough, there must be another reference kept internally somewhere.

Anyway, here is the hack:

private void DisposeImage(BitmapImage image)
{
    if (image != null)
    {
        try
        {
            using (var ms = new MemoryStream(new byte[] { 0x0 }))
            {
                image.SetSource(ms);
            }
        }
        catch (Exception)
        {
        }
    }
}

You can call it for instance in your RefreshImage method:

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

Kinda ashamed to use that, but at least it seems to work.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...