Memory Leak With Simple App

LorenzettiLorenzetti BRMember ✭✭
edited April 2013 in Xamarin.Android

I have a simple app that calls a method in the click event of the button.

As I'm clicking the button the use of memory is increasing and it is not released.

I suspect this problem is related to the use of List. The Clear() method seems not release the memory.

Android 4.0.1
MonoAndroid 4.4.54
Visual Studio 2010
Java 7.17

The below code is a simple example. In actual application the consumption is much higher.

Activity1.cs

public class Activity1 : Activity
{
    Class1 class1;

    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        SetContentView(Resource.Layout.Main);

        Button button = FindViewById<Button>(Resource.Id.MyButton);

        class1 = new Class1();

        button.Click += new EventHandler(button_Click);
    }

    void button_Click(Object sender, EventArgs e)
    {
            class1.Pesquisa();
    }
}

Main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
<Button  
    android:id="@+id/MyButton"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/Hello"/>
</LinearLayout>

Class1.cs

class Class1
{
    List<ListClass> list;

    public Class1()
    {
        list = new List<ListClass>();           
    }

    public void Pesquisa()
    {
        list.Clear();

        for (Int32 i = 0; i < 5000; i++)
            list.Add(new ListClass(i.ToString(), i, i, i, i));
    }
}

ListClass.cs

class ListClass
{
    public ListClass(String word, Double double1, Double double2, Double double3, Double double4)
    {
        this.word = word;
        this.double1 = double1;
        this.double2 = double2;
        this.double3 = double3;
        this.double4 = double4;
    }

    public String word
    {
        get;
        set;
    }

    public Double double1
    {
        get;
        set;
    }

    public Double double2
    {
        get;
        set;
    }

    public Double double3
    {
        get;
        set;
    }

    public Double double4
    {
        get;
        set;
    }
}

Posts

  • IvanHarrisIvanHarris USMember ✭✭✭

    This is what I see you're doing here.
    Each time you click the button this happens:
    1. Clears whatever is in the list
    2. populates list with 5000 objects.

    list.Clear(); <-- you're trying to clear the objects BEFORE you populate the list again right? Cause thats what you're doing here.

    Ill test the code in a few hours and let you know what I found.

  • LorenzettiLorenzetti BRMember ✭✭
    edited April 2013
    In the real application I fill the list with data from the database according to the parameters passed.

    The user can change the parameter and load the list using another parameter. So I always cleaned at the start of the method.
  • YamilBrachoYamilBracho VEMember

    Maybe you have to clear out the objects referenced insidie the lsit, I mean:

    public void Pesquisa()
    {
    foreach (ListClass lc in list) {
    IDisposable disp = lc as IDisposable;
    if (disp != null)
    {
    disp.Dispose();
    }
    }

        list.Clear();
    
        for (Int32 i = 0; i < 5000; i++)
            list.Add(new ListClass(i.ToString(), i, i, i, i));
    }
    
  • JonathanPryorJonathanPryor USXamarin Team Xamurai

    As I'm clicking the button the use of memory is increasing and it is not released.

    How are you tracking memory use? Which memory is constantly increasing and isn't released?

    I suspect this problem is related to the use of List<T>. The Clear() method seems not release the memory.

    This is somewhat correct: List<T>.Clear() doesn't release the underlying array, it just clears the contents:

        public void Clear ()
        {
            Array.Clear (_items, 0, _items.Length);
            _size = 0;
            _version++;
        }
    

    The end result being that after your list.Clear() call, list._items will still contain 5000+ (8192, actually) elements, they'll just all be null. You will need to perform/wait for a GC for the 5000 previously allocated instances to be collected.

    In the case of your sample app, this behavior is actually an optimization. The first time Class1.Pesquisa() is invoked, the Class1.list._items array will need to be constantly resized until it contains at least 5000 elements (due to the for loop). (It'll actually contain 8192 elements due to the "double the previous size" behavior.) The second and subsequent times Class1.Pesquisa() is invoked, Class1.list._items will not need to be changed at all, and you're just allocating 5000 ListClass instances (instead of 5000 ListClass instances + log2(5000) ListClass[] array instances).

    This is also the documented behavior:

    Capacity remains unchanged. To reset the capacity of the List<T>, call the TrimExcess() method or set the Capacity property directly.

    If you absolutely need to shrink your List<T> size, you can't just use List<T>.Clear().

    By any chance, is your "real" ListClass type a subclass of Java.Lang.Object?

  • LorenzettiLorenzetti BRMember ✭✭

    The total of memory monitored through the Debug.NativeHeapAllocatedSize is always increasing.

    But the Java.Lang.Runtime.GetRuntime().TotalMemory() is constant in 7MB.

    What's the difference?

  • JonathanPryorJonathanPryor USXamarin Team Xamurai

    Runtime.TotalMemory() is the amount of "managed" memory that Dalvik is using. It is equivalent in spirit to GC.GetTotalMemory(forceFullCollection:false).

    Debug.NativeHeapAllocatedSize, meanwhile, is the size of "unmanaged memory" from Dalvik's perspective. This would include Mono's GC heap, any non-GC Mono-managed memory, and any memory that native libraries have allocated in your process (if you've included any other native libraries with your app), e.g. OpenTK buffers.

Sign In or Register to comment.