mapbox / mapbox-maps-flutter

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

PointAnnotationManager uses excessive memory #496

Open noforeignland opened 7 months ago

noforeignland commented 7 months 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 7 months 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 7 months 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.

baleboy commented 3 months ago

Hi,

Point annotations aren't suitable for such a large amount of annotations, we don't recommend using them for more than 250 annotations.

For your number of annotations, Style Layers with GeoJson sources are the recommended way. Regarding the limitations you have found, 1 & 2 are valid. In the native SDK we introduced partial GeoJSON updates to address them, but this API is not exposed in Flutter. We'll plan it for one of the upcoming releases. For 3, you should be able to get an id of a feature from the identifier property.