Google Maps leaking?

MihaMarkicMihaMarkic ✭✭✭✭SI ✭✭✭✭

Can somebody double check my sample that shows Google Maps leaking like a boss?
The sample is as simple as possible, using 4.2 as minimum Android version to avoid using support libraries.

It creates a button and a MapFragment and then places a large bitmap on map (to speed up memory leak). Upon pause the MapFragment is removed. When activity is recreated (destroyed and then created) the memory usage goes up and keeps going up until it crashes eventually. I tested it by rotating the emulator/device but it all boils down to activity recreation.

Perhaps I am doing something obviously wrong? Note that even without placing a huge bitmap on it it'd eventually leak (by smaller steps). I've also tried some variations but all leaked. I was using DDMS's HEAP to see that both allocated memory and #objects are growing.

To run the sample you'd need to use your Google Maps key and place it into manifest and allow com.rthand.android.testmapleak package to access the service.

here is the code as well:

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

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);
        InitMap();
    }

    private MapFragment mapFragment;
    private void InitMap()
    {
        GoogleMapOptions mapOptions = new GoogleMapOptions()
                .InvokeMapType(GoogleMap.MapTypeNormal)
                .InvokeZoomControlsEnabled(false)
                .InvokeCompassEnabled(true);
        FragmentTransaction fragTx = FragmentManager.BeginTransaction();
        mapFragment = MapFragment.NewInstance(mapOptions);
        fragTx.Replace(Resource.Id.map, mapFragment);
        fragTx.Commit();
    }

    private GoogleMap map;
    private Marker marker;
    protected override void OnResume()
    {
        base.OnResume();
        map = mapFragment.Map;
        SetBitmaps();
    }

    protected override void OnPause()
    {
        base.OnPause();
        if (marker != null)
        {
            marker.Remove();
        }
        map.Clear();
        FragmentTransaction fragTx = FragmentManager.BeginTransaction();
        fragTx.Remove(mapFragment);
        mapFragment.Dispose();
        fragTx.Commit();
    }

    private void SetBitmaps()
    {
        Stream iso = Assets.Open("image.jpg");
        Bitmap bitmap = BitmapFactory.DecodeStream(iso);
        MarkerOptions poiOptions = new MarkerOptions()
                .SetPosition(new LatLng(46.055875, 14.350028))
                .InvokeIcon(BitmapDescriptorFactory.FromBitmap(bitmap));
        marker = map.AddMarker(poiOptions);
        bitmap.Recycle();
        bitmap.Dispose();
    }`

Posts

  • MihaMarkicMihaMarkic ✭✭✭✭ SI ✭✭✭✭

    Ah, I am using 4.12.4 (latest beta) and Google Play Services (Jelly Bean) 16.0 component.

  • PeterDavisPeterDavis ✭✭✭ USMember ✭✭✭
    edited May 2014

    I see several issues here:

    First of all, your create a new mapFragment in InitMap() called by OnCreate(), and you dispose of it in OnPause(), but you never get a new instances if you resume. I'd expect the map = mapFragment.Map line in OnResume() to fail. I certainly wouldn't trust what it returns after OnPause() is called.

    In SetBitmaps(), you don't dispose of the Stream iso. You should use the using keyword. Then you don't have to worry abotu the Dispose() calls and more importantly, they'll get disposed even if an exception is thrown in the code.

    private void SetBitmaps()
    {
        using(Stream iso = Assets.Open("image.jpg"))
        using(Bitmap bitmap = BitmapFactory.DecodeStream(iso))
        {
            MarkerOptions poiOptions = new MarkerOptions()
                    .SetPosition(new LatLng(46.055875, 14.350028))
                    .InvokeIcon(BitmapDescriptorFactory.FromBitmap(bitmap));
            marker = map.AddMarker(poiOptions);
        }
    }
    
  • MihaMarkicMihaMarkic ✭✭✭✭ SI ✭✭✭✭

    Hi Peter,

    You are right, the code is really a dumbed down version to test this particular scenario (recreation, not fitted for resume at all).
    And yes, I've omitted iso.Dispose(), it should be there. Although I doubt it does anything in this regard.
    Even using Dispose() (what using is doing behind the scenes) on iso doesn't help. Huge leak is still there.

    Thanks for suggestions though.

  • MihaMarkicMihaMarkic ✭✭✭✭ SI ✭✭✭✭

    Looking at Google Play Services 16.0 component docs, at the bottom. This scares me for good:

    Troubleshooting

    In order to fix java.lang.OutOfMemoryError: Java heap space you will need to increase the JavaMaximunHeapSize, in order to do this you must edit the .csproj (Your Project File) in a XML/Text editor, and add

    1G

    •JavaMaximunHeapSize - This allows the overriding of the default java heapsize which is sometimes too low to compile some .jar files.

    Eeee, to fix memory leaks (known???) one has to increase heap size? And then, when memory hits that limit? Dafuq, xamarin.

  • MihaMarkicMihaMarkic ✭✭✭✭ SI ✭✭✭✭

    OK, looks like mystery solved thanks to Jonathan Pryor. It is a GC issue since monodroid doesn't know about the java memory pressure. Doing manual GC does the trick. At least after a preliminary testing. Phew, it makes total sense...

  • FreakyAliFreakyAli ✭✭ USMember ✭✭

    @MihaMarkic
    How Exactly did you solve this issue Please show us as well.

  • MihaMarkicMihaMarkic ✭✭✭✭ SI ✭✭✭✭

    @Coldbloodedkiller As I said :) you should do GC.Collect(1) (or GC.Collect() to be on the safe side) when ever activity (map) is recreated. That way the link between xamarin and java will be broken and OS will be able to dispose it.

  • FreakyAliFreakyAli ✭✭ USMember ✭✭

    Thanks That Helped. @MihaMarkic

Sign In or Register to comment.