Should the GC be called manually or be called automatically?

philiphuynhphiliphuynh USMember ✭✭
edited April 2017 in Xamarin.Android

I have an application with a RecyclerView consisting of ImageViews inside of it. I've noticed in the Xamarin Profiler that the memory usage continues to rise until the application gets the out of memory error without the GC ever being invoked automatically by the system.

If I were to call the GC manually the memory usage would decrease but this would the UI to stutter for a bit. So my question is shouldn't the GC have been called automatically when the memory usage was getting too high or is there an option I have to set in order to make it work properly?

Answers

  • LjusnanLjusnan DEMember ✭✭✭

    There should be no need to call the GC manually in most cases. Are you working with big images? Can you show us some code of your RecyclerView and the adapter?

  • philiphuynhphiliphuynh USMember ✭✭
    edited April 2017

    Thank you for your reply. I am not working with big images as I try to limit their size to around 50kB and resolution to 300x200px.

    Here are the code to my RecyclerAdapter and ViewHolder:

    public class RecyclerAdapterService : RecyclerView.Adapter
            {
                private Bitmap[] imageList;
    
                public RecyclerAdapterService(Bitmap[] imageList)
                {
                    this.imageList = imageList;
                }
    
                public override int ItemCount
                {
                    get
                    {
                        return imageList.Length;
                    }
                }
    
                public override void OnViewRecycled(Java.Lang.Object holder)
                {
                    base.OnViewRecycled(holder);
                    ServiceViewHolder vh = holder as ServiceViewHolder;
                    vh.Image.SetImageBitmap(null);
                }
    
                public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
                {
                    ServiceViewHolder vh = holder as ServiceViewHolder;
                vh.Image.SetImageBitmap(imageList[position]);
                }
    
                public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
                {
                    View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.CardViewServiceItem, parent, false);
                    ServiceViewHolder vh = new ServiceViewHolder(itemView);
                    return vh;
                }
    
                public class ServiceViewHolder : RecyclerView.ViewHolder
                {
                    public CardView CardView { get; set; }
                    public ImageView Image { get; set; }
    
                    public ServiceViewHolder(View itemView) : base(itemView)
                    {
                        // Locate and cache view references:
                        CardView = itemView.FindViewById<CardView>(Resource.Id.cardViewService);
                        Image = itemView.FindViewById<ImageView>(Resource.Id.cardViewServiceImage);
                    }
                }
            }
        }
    

    And outside in my fragment I create a new instance of the RecyclerAdapter and set my RecyclerView adapter to that new instance:

    RecyclerAdapter ra = new RecyclerAdapter(imageList);
    recyclerView.SetAdapter(ra);
    recyclerView.SetLayoutManager(new LinearLayoutManager(Context, LinearLayoutManager.Horizontal, false));
    
  • BerayBentesenBerayBentesen TRUniversity ✭✭✭✭

    @philiphuynh use Glide for lazy image loading.

  • philiphuynhphiliphuynh USMember ✭✭
    edited April 2017

    @BerayBentesen said:
    @philiphuynh use Glide for lazy image loading.

    Thank you for your suggestion Beray. I have tried incorporating the Glide library into the RecyclerViewAdapter and the same result happens.

    As I scroll back and forth on the RecyclerView I could see the memory usage of the application continues to rise and rise until it hits a certain point and crashes with the out of memory exception. The GC did not execute once during this time.

    How I loaded up the image was like this in the OnBindViewHolder:

    Glide.With(context).Load(imageList[position]).Into(vh.Image);

  • BerayBentesenBerayBentesen TRUniversity ✭✭✭✭

    @philiphuynh It is because you're still collecting bitmaps in a list. Change List object type as String and give it an url to confirm that it is because of Bitmaps.

  • philiphuynhphiliphuynh USMember ✭✭

    @BerayBentesen said:
    @philiphuynh It is because you're still collecting bitmaps in a list. Change List object type as String and give it an url to confirm that it is because of Bitmaps.

    I will check what you just said to confirm it.

    But in my real application I am getting a List of byte arrays containing images from a database and I would display them in the RecyclerView. So the GC would not work properly in this situation?

  • BerayBentesenBerayBentesen TRUniversity ✭✭✭✭

    @philiphuynh If you're getting list of images from database, why you don't return url instead of byte arrays ? For your question, you're receiving OOM error which is related with Java heap space not GC.

  • philiphuynhphiliphuynh USMember ✭✭

    @BerayBentesen said:
    @philiphuynh If you're getting list of images from database, why you don't return url instead of byte arrays ? For your question, you're receiving OOM error which is related with Java heap space not GC.

    Thank you for your quick replies. I apologize for asking but currently the images are stored in a table of a MS SQL database and the data is of type varbinary(MAX). Is there a way to convert this data into a url?

Sign In or Register to comment.