mapbox / mapbox-maps-flutter

Interactive, thoroughly customizable maps for Flutter powered by Mapbox Maps SDK
https://www.mapbox.com/mobile-maps-sdk
Other
245 stars 93 forks source link

PointAnnotationManager uses excessive memory #496

Open noforeignland opened 1 month ago

noforeignland commented 1 month ago

In the demo application, increasing the loop size in the PointAnnotationPage to 10K markers:

  for (var i = 0; i < 10000; i++) {
    options.add(PointAnnotationOptions(
        geometry: createRandomPoint().toJson(), image: list));
  }

causes the demo application to crash with the following OOM error:

E/AndroidRuntime( 4299): FATAL EXCEPTION: main
E/AndroidRuntime( 4299): Process: com.mapbox.maps.flutter.example, PID: 4299
E/AndroidRuntime( 4299): java.lang.OutOfMemoryError: Failed to allocate a 26640400 byte allocation with 11372088 free bytes and 10MB until OOM, target footprint 268435456, growth limit 268435456
E/AndroidRuntime( 4299):    at java.util.Arrays.copyOf(Arrays.java:3161)
E/AndroidRuntime( 4299):    at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
E/AndroidRuntime( 4299):    at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
E/AndroidRuntime( 4299):    at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
E/AndroidRuntime( 4299):    at io.flutter.plugin.common.StandardMessageCodec.writeBytes(StandardMessageCodec.java:196)
E/AndroidRuntime( 4299):    at io.flutter.plugin.common.StandardMessageCodec.writeValue(StandardMessageCodec.java:247)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessengerCodec.writeValue(PointAnnotationMessenger.kt:738)
E/AndroidRuntime( 4299):    at io.flutter.plugin.common.StandardMessageCodec.writeValue(StandardMessageCodec.java:277)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessengerCodec.writeValue(PointAnnotationMessenger.kt:738)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessengerCodec.writeValue(PointAnnotationMessenger.kt:724)
E/AndroidRuntime( 4299):    at io.flutter.plugin.common.StandardMessageCodec.writeValue(StandardMessageCodec.java:277)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessengerCodec.writeValue(PointAnnotationMessenger.kt:738)
E/AndroidRuntime( 4299):    at io.flutter.plugin.common.StandardMessageCodec.writeValue(StandardMessageCodec.java:277)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessengerCodec.writeValue(PointAnnotationMessenger.kt:738)
E/AndroidRuntime( 4299):    at io.flutter.plugin.common.StandardMessageCodec.encodeMessage(StandardMessageCodec.java:76)
E/AndroidRuntime( 4299):    at io.flutter.plugin.common.BasicMessageChannel$IncomingMessageHandler$1.reply(BasicMessageChannel.java:266)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessenger$Companion$setUp$2$1$1.invoke(PointAnnotationMessenger.kt:843)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessenger$Companion$setUp$2$1$1.invoke(PointAnnotationMessenger.kt:837)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.annotation.PointAnnotationController.createMulti(PointAnnotationController.kt:81)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessenger$Companion.setUp$lambda-3$lambda-2(PointAnnotationMessenger.kt:837)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessenger$Companion.$r8$lambda$SImXOqSBSxtubGcdXZLjwzzH03A(Unknown Source:0)
E/AndroidRuntime( 4299):    at com.mapbox.maps.mapbox_maps.pigeons._PointAnnotationMessenger$Companion$$ExternalSyntheticLambda2.onMessage(Unknown Source:2)
E/AndroidRuntime( 4299):    at io.flutter.plugin.common.BasicMessageChannel$IncomingMessageHandler.onMessage(BasicMessageChannel.java:261)
E/AndroidRuntime( 4299):    at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:292)
E/AndroidRuntime( 4299):    at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:319)
E/AndroidRuntime( 4299):    at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
E/AndroidRuntime( 4299):    at android.os.Handler.handleCallback(Handler.java:938)
E/AndroidRuntime( 4299):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 4299):    at android.os.Looper.loop(Looper.java:233)
E/AndroidRuntime( 4299):    at android.app.ActivityThread.main(ActivityThread.java:8063)
E/AndroidRuntime( 4299):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 4299):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
E/AndroidRuntime( 4299):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978)

Expected bahaviour:

The application should be able to cope with tens of thousands of markers, not just a small number. Our production app uses Flutter mapbox_gl and manages in excess of 70K markers simultaneously.

pjleonard37 commented 1 month ago

Hi @noforeignland --

Thanks for sharing this report -- we will investigate on our side.

There are a few different approaches to adding annotations/markers, so I want to make sure you are using the best approach for your needs. Can you share a bit more information about your use case and how you are creating the annotations? Is the data for these annotations stored in a GeoJSON or some other format?

noforeignland commented 1 month ago

We are trying to migrate from mapbox_gl to flutter_mapbox_maps. Currently our app pushes about 70K GeoJson markers onto the mapbox map. It seems to handle it very easily.

Using the new PointAnnotationOptions interface in flutter_mapbox_maps has two main problems:

1) There's a pretty low limit on how many markers can be added before an OOM error is thrown and the app dies. 2) When the user taps on a marker, the ID that is provided is a GUID that cannot be overidden, makgin it impossible for us to tell which feature was tapped on on the map.

We have been trying out adding a GeoJson source directly but this also has problems:

1) It's slow to refresh the markers 2) There is no way of changing the data source once it's set - an exception is thrown when changes are made 3) The onTap event does not indicate which feature is tapped on and the coordinates provided do not return results when queried. See my post in Discord for more details.