Bug 21454 - ListView performing very slowly with many columns of TextView in each row.
Summary: ListView performing very slowly with many columns of TextView in each row.
Alias: None
Product: Android
Classification: Xamarin
Component: BCL Class Libraries ()
Version: 4.12.4
Hardware: PC Mac OS
: Normal normal
Target Milestone: ---
Assignee: Jonathan Pryor
Depends on:
Reported: 2014-07-18 20:53 UTC by Jon Goldberger [MSFT]
Modified: 2014-07-23 04:38 UTC (History)
3 users (show)

Is this bug a regression?: ---
Last known good build:

Original test Project without using view holder pattern (17.34 KB, application/zip)
2014-07-18 20:53 UTC, Jon Goldberger [MSFT]
Test Project with View Holder Pattern (26.40 KB, application/zip)
2014-07-18 20:57 UTC, Jon Goldberger [MSFT]

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report for Bug 21454 on Developer Community or GitHub if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: Developer Community HTML or GitHub Markdown
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:

Comment 1 Jon Goldberger [MSFT] 2014-07-18 20:57:11 UTC
The list view performance is very slow in both of the attached test projects, one which uses the view holder patter and one which does not. 

This appears to be due to a very high greg count and it seems that the grefs are not being released properly when the GetView method exits. 

To reproduce:

1. Load either test project.
2. Launch on device/emulator
3. Sroll listview.

Expected result: a smooth scrolling list view.
Actual result: a virtually unusable list view.

See case file in private comment above for more information and steps the developer has taken to try to track down the issue.
Comment 2 Jon Goldberger [MSFT] 2014-07-18 20:57:49 UTC
Created attachment 7428 [details]
Test Project with View Holder Pattern
Comment 3 Jon Goldberger [MSFT] 2014-07-18 20:58:47 UTC
PS, in the test project with the view holder pattern, I also tried to use a layout file instead of creating a layout programmatically. This did seem to help a bit, but not very much.
Comment 4 David Schulte 2014-07-18 22:14:57 UTC
Besides the performance issue, our main concern is gref consumption. We cannot use the "ViewHolder" approach/pattern because of the number of grefs that would be held (for a ListView containing 50 columns of TextViews with, let's say, 10 rows of visible data, we would see the potential for 500 TextViews. When you take into account our need to resize TextViews programmatically based on data that appears in the column (we want to size the TextViews to be as wide as the widest value), the gref count grows even larger due to needing to allocate LayoutParameter objects and assign them to each TextView's LayoutParameters member when allocating new TextViews for inclusion in the ListView as a result of seeing GetView()'s convertView member being null.

In our case, the ListView is list of horizontal LinearLayouts, with each layout consisting of TextViews, one for each column.

To reduce the number of grefs that are actively held our ListView implementation, we have chosen to invoke Dispose() the TextView references obtained while in GetView(), whether instantiated due to the convertView being empty, or as a result of invoking FindViewById() against convertView (we only allocate a new LinearLayout and new TextViews for the row when GetView()'s convertView member is null - otherwise we invoke FindViewById() against the LinearLayout passed to us to get a reference to each TextView in the LinearLayout). Our concerns with this approach, however, include a) is it a safe to invoke Dispose() in this way, and b) why do we need to invoke Dispose() explicitly in the first place - why doesn't leaving the scope in which a TextView reference (gref) has been instantiated either through a call to "new TextView(context)' or "FindViewById(Resource.Id.XXX)" cause the gref to be disposed of automatically.

Through additional testing, we found that we can also dispose of the TextView's LayoutParameters member which we either instantiate in GetView() if convertView is null, or get back as a result of invoking FindViewById(), given that we instantiate one before adding the view to the LinearLayout (see the sample project posted by Jon), without any apparent adverse affects. Invoking FindViewById() against the LinearLayout passed to us by GetView() via its convertVIew parameter when not null returns a TextView with its LayoutParameters member assigned. By calling Dispose() on the TextView's LayoutParameters member before invoking Dispose() on the TextView reference results in even more gref savings. This, however, poses another question - why doesn't disposing of the TextView also dispose of the TextView's members, the LayoutParameters member in this case?

Again, while we are concerned with the poor scrolling quality, we are more concerned with gref consumption, why grefs are not being disposed of automatically, and whether our explicit use of Dispose() from within GetView() is a safe thing to do, presuming that we are not keeping a reference to the given gref someplace else.

I had posted another question to Jon as well, in that we recognize that invoking FindViewById() will return a pre-existing gref if one exists, instead of allocating a new one. However, if you invoke Dispose() on one such gref while another exists, the iIspose() ends up affecting both references. What surprised me about this is that Xamarin does not seem to maintain a reference count inside each gref that would tell it if a call to Dispose() should really dispose of the gref.

Anyway, Jon has my personal credentials if you would like to contact me by email or phone if you need additional information.
Thanks in advance for looking at this case.
Dave Schulte
Comment 5 Ram Chandra 2014-07-22 06:46:46 UTC
I tried to reproduce this issue with the attached sample and I am also getting the same behavior.

When I try to scroll the listview I observed that listview is not scrolling smoothly. I have checked both the attached project one which is not using view holder pattern and another on which is using the view holder pattern.

I have checked this issue on both device and emulator and I am getting the same behavior on both device and emulator.

Screencast: http://www.screencast.com/t/gQdwVGnLkTS

Environment Info:

Mac OS X 10.9.3
Xamarin Studio: 5.1.4 (build 0)
Xamarin.Android: 4.12.6 

Build Information
Release ID: 501040000
Git revision: 7d45bbe2ee22625f125d0c52548524f02d005cca
Build date: 2014-07-14 12:15:53-04
Xamarin addins: c78f1d88e57baa928aeee1484d96e6f8edf8de33
Comment 6 David Schulte 2014-07-22 09:35:43 UTC
Again, please also consider our concerns with grefs and having to explicitly dispose of them (i.e., the TextView and LayoutParameter references in my original example), instead of Xamarin disposing of them automatically upon leaving the scope of a given block or method.