firebase / firebase-android-sdk

Firebase Android SDK
https://firebase.google.com
Apache License 2.0
2.26k stars 572 forks source link

[Storage] java.lang.OutOfMemoryError after repeated use of putData()/putFile() #6310

Open CloudMountain opened 4 days ago

CloudMountain commented 4 days ago

[REQUIRED] Step 2: Describe your environment

Android Studio Hedgehog | 2023.1.1 Patch 2 Build #AI-231.9392.1.2311.11330709, built on January 18, 2024 Runtime version: 17.0.7+0-17.0.7b1000.6-10550314 aarch64 VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o. macOS 14.4.1 GC: G1 Young Generation, G1 Old Generation Memory: 2048M Cores: 8 Metal Rendering is ON Registry: external.system.auto.import.disabled=true debugger.watches.in.variables=false ide.text.editor.with.preview.show.floating.toolbar=false

Non-Bundled Plugins: Dart (231.9411) io.flutter (77.2.1)

[REQUIRED] Step 3: Describe the problem

I originally posted about a problem I was having in the flutterfire repo here: https://github.com/firebase/flutterfire/issues/13385, and was told this was an problem with the native sdk and to raise a new issue here. Apologies in advance as I have limited experience in writing native code, and so can only really present the issue using Flutter terminology.

As can be seen in the original issue, the problem I am having is that I am trying to upload byte buffers of ~5MB on the order of a few hundred times during the course of a user session. Some users are experiencing OOM crashes of the following form: Fatal Exception: java.lang.OutOfMemoryError: Failed to allocate a 2576 byte allocation with 1020864 free bytes and 996KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC.

After doing some memory profiling with Android Studio, I was able to narrow down the issue to calls of the ref().putData()and/or ref().putFile()functions from the firebase_storage flutter plugin.

Steps to reproduce:

Make a button in a flutter app which simply creates a Uint8List of length 5,000,000 and then uploads it using either putData() or putFile(). Recording Java/Kotlin allocations whilst tapping that button shows the following byte buffers are allocated and then never deallocated: Screenshot 2024-09-23 at 16 11 54 Screenshot 2024-09-23 at 16 12 19

Relevant Code:

Uint8List buffer = Uint8List(5000000);
Reference ref =  FirebaseStorage.instance.ref();
UploadTask? uploadTask = await ref.child(path/to/file.pcm).putData(buffer);
lehcar09 commented 3 days ago

Hi @CloudMountain, thank you for reaching out. The Firebase Storage putBytes() or putData() (in Flutter) and putFile() are asynchronous methods. It returns a Taskobject that you can use to monitor the upload progress and handle its completion or failure.

Calling multiple putBytes methods in Firebase Storage without proper management can potentially lead to Out-of-Memory (OOM) exceptions, especially on devices with limited RAM.

Are you calling the putBytes() concurrently? If there are 50 concurrent calls with 5MB file size to upload, it's possible that the memory allocation used for this operation is more than the memory available on your device

I tried reproducing the issue and I was able to encounter the OOM exception on the 32nd concurrent call. Is there any chance you can share a bit more code snippet on how you did the “upload byte buffers of ~5MB on the order of a few hundred times during the course of a user session”? Thanks!