locked
UWP: Images in GridView cause memory leaks RRS feed

  • Question

  • Hello,

    I've developed an UWP app with MvvmLight for managing forms. Each form can contain a lot of images. These images raise memory leaks, and I don't found any solution to fix this...

    Currently all the datas are stored in a SQLite database, including the images, as byte[].

    The root object is a "Car_Form", which contains the "Images" in an ObservableCollection:

    public class Car_Forms : BasePoco
    {
        // form_id
        private int _form_id;
        [PrimaryKey, NotNull, AutoIncrement]
        public int form_id
        {
            get
            {   return _form_id; }
            set
            {
                if (value != _form_id)
                {
                    _form_id = value;
                    RaisePropertyChanged(() => form_id);
                }
            }
        }
    
        //...
    
        // images
        private ObservableCollection<Images> _images;
        [Ignore]
        public ObservableCollection<Images> images
        {
            get
            { return _images; }
            set
            {
                if (value != _images)
                {
                    _images = value;
                    RaisePropertyChanged(() => images);
                }
            }
        }
    }

    The "Images" object contains the reference to the "Car_Form", the byte[] that is stored in the SQLite database in the "image1" field, and the BitmapImage that is used for the display, in the "image_display" field:

    public class Images : BasePoco
    {
        // image_id
        private int _image_id;
        [PrimaryKey, NotNull, AutoIncrement]
        public int image_id
        {
            get
            { return _image_id; }
            set
            {
                if (value != _image_id)
                {
                    _image_id = value;
                    RaisePropertyChanged(() => image_id);
                }
            }
        }
    
        //...
    
        // image1
        private byte[] _image1;
        [NotNull]
        public byte[] image1
        {
            get
            { return _image1; }
            set
            {
                if (value != _image1)
                {
                    _image1 = value;
                    RaisePropertyChanged(() => image1);
                }
            }
        }
    
        // form_id
        private int? _form_id;
        public int? form_id
        {
            get
            { return _form_id; }
            set
            {
                if (value != _form_id)
                {
                    _form_id = value;
                    RaisePropertyChanged(() => form_id);
                }
            }
        }
    
        // bitmap_image
        private BitmapImage _bitmap_image;
        [Ignore]
        public BitmapImage bitmap_image
        {
            get
            { return _bitmap_image; }
            set
            {
                if (value != _bitmap_image)
                {
                    _bitmap_image = value;
                    RaisePropertyChanged(() => bitmap_image);
                }
            }
        }
    }

    In my XAML page, the "Images" are in displayed in a GridView like this:

    <GridView ItemsSource="{x:Bind ViewModel.CarForm.images, Mode=OneWay}"
              IsItemClickEnabled="True"
              SelectionMode="Single"
              Grid.Row="1">
        <GridView.ItemTemplate>
            <DataTemplate x:DataType="models:Images">
                <Border BorderBrush="Gray" BorderThickness="2" 
                        Background="White"
                        Padding="10"
                        Height="160" Width="225">
                    <Image Stretch="UniformToFill"
                           Source="{x:Bind image1, Mode=OneWay, Converter={StaticResource ByteArrayToBitmapImageConverter}}" />
                </Border>
            </DataTemplate>
        </GridView.ItemTemplate>
    </GridView>

    The datas are loaded from the SQLite database in the ViewModel:

    private Car_Forms _carForm;
    public Car_Forms CarForm
    {
        get { return _carForm; }
        set
        {
            this._carForm = value;
            RaisePropertyChanged(() => CarForm);
        }
    }
    
    private void LoadForm(Guid id)
    {
        CarForm = RepositoryService.GetById<Car_Forms>(id);            
        var formImages = RepositoryService.Where<Images>(im => im.IsDeleted == false && im.form_id == CarForm.form_id);
        CarForm.images = new ObservableCollection<Images>(formImages);
        //...
    } 

    Then, the the BitmapImage is loaded through the "ByteArrayToBitmapImageConverter":

    public class ByteArrayToBitmapImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            try
            {
                var bImg = (byte[])value;
                if (bImg != null)
                {
                    BitmapImage biImg = ByteArrayBitmapHelper.AsBitmapImage(bImg);
                    return biImg;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception e)
            {
                return null;
            }
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            return null;
        }
    }
    
    public static BitmapImage AsBitmapImage(this byte[] byteArray)
    {
        if (byteArray != null)
        {
            using (var stream = new InMemoryRandomAccessStream())
            {
                stream.WriteAsync(byteArray.AsBuffer()).GetResults(); 
                var image = new BitmapImage();
                stream.Seek(0);
                image.SetSource(stream);
                return image;
            }
        }
        return null;
    }

    I have also implemented a "Cleanup()" method on the ViewModel, where I "clean" all the objects that are used:

    public override void Cleanup()
    {
        //...
        CarForm.images.Clear();
        CarForm = null;
        base.Cleanup();
    }

    But when I launch the app, I can see that all resources are not released: each time that I open the same form and that I come back to the list, there are 30 Mo that are not released.

    MemoryLeaks

    1. laucnh of the app: "Home" page
    2. navigate and display of the form
    3. back to the "Home" page
    4. navigate and display of the form
    5. back to the "Home" page
    6. navigate and display of the form
    7. back to the "Home" page
    8. navigate and display of the form
    9. back to the "Home" page

    I've looked here that it is possible to use DecodePixelWidth/DecodePixelHeight: but as my BitmapImage is generated from a Converter, I don't see how to use it. In add, my Converter is use each time that I display an image: in "thumbnail " or in full size...

    And I've also tried to "comment" the piece of code that contains the Image in the Border:

               <Border BorderBrush="Gray" BorderThickness="2" 
                        Background="White"
                        Padding="10"
                        Height="160" Width="225">
                    <!--<Image Stretch="UniformToFill"
                           Source="{x:Bind image1, Mode=OneWay, Converter={StaticResource ByteArrayToBitmapImageConverter}}" />-->
                </Border>

    It didn't change anything: the use of the memory is the same...

    Would you have any explanation? How could I optimize this?

    Wednesday, February 8, 2017 10:32 AM

All replies

  • Hi pcdus,

    I am trying to reproduce your issue and do some test and research now. It may be some time delay. Appreciate your patience.

    Best regards,
    Breeze Liu

    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, February 10, 2017 5:44 AM