Monodroid GREF problem best practice?

2020-04-27 13:01发布

问题:

I have the following test code (based on standard monodroid HelloWorld)

namespace TestGREF
{
    [Activity (Label = "TestGREF", MainLauncher = true)]
    public class Activity1 : Activity
    {
        int count = 1;      
        protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);
            SetContentView (Resource.Layout.Main);
            Button button = FindViewById<Button> (Resource.Id.myButton);

            button.Click += delegate {
                button.Text = string.Format ("{0} clicks!", count++); 
                for(int i=0;i<10000;i++){
                    new Java.Lang.Object(new System.IntPtr(i));
                    //...some stuff here. Instead of Java.Lang.Object may be
                    //something much more useful.
                }

                //If uncomment here, looks ok
                //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            };
        }
    }
}

If I click the button 5-6 times, application crashes.

I know this happens because of global refences (GREF) limit (described here, "Unexpected NullReferenceExceptions" section). The question is: what to do with it? What is the best practice? If possible, with code example please

If uncomment GC.Collect() call, all seems working, but calling GC too often is too exspensive for performance. Another popular design is to put new statement put of loop, but it is not always possible cause of program logic.

Any more ideas?

回答1:

You need to release all unmanaged objects when they no longer needed. All classes that inherits from Android.Runtime.IJavaObject also inherits IDisposable so you need to dispose them.

Here is part from my project

private Spinner _spType;
private ArrayAdapter _arrayAdapter;

protected override void OnCreate(Android.OS.Bundle savedInstanceState)
{
  base.OnCreate(savedInstanceState);
  _spType = FindViewById<Spinner>(Resource.Id.spinnerType);
  _arrayAdapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleSpinnerItem, new[] {"1","2","3","4","5"});
  _spType.Adapter = _arrayAdapter;
}

public override void Finish()
{
    if (_spType != null)
        _spType.Dispose();
    if (_arrayAdapter != null)
        _arrayAdapter.Dispose();
    base.Finish();
}


回答2:

for(int i=0;i<10000;i++){
    var obj = new Java.Lang.Object(new System.IntPtr(i));

    //...some stuff here. Instead of Java.Lang.Object may be
    //something much more useful.

    obj.Dispose(); //Deletes an object and GREF too. 
    //Cannot be used if object is still used in dalvik VM
}

If you cannot use Dispose() (for example, unmanaged object is a part of layout, which will be used by android lated, but not by C# code), use GC.Collect() wisely. GC.Collect() kills all the GREFs to variables, which are out of usage by Mono Environment and out of current scope.



回答3:

Here is article about GC and memory management in monodroid. It can be helpful for you http://docs.xamarin.com/android/advanced_topics/garbage_collection