Search code examples
iosswiftrealmkotlin-multiplatformkotlin-native

KMM Memory Leak (RAM + Disk) issue when using MongoDB Realms on iOS


I have been using Java Realm in the past for an Android app, that I migrated recently to Kotlin Multiplatform in order to make it compatible with iOS. After testing, it appears that by running it in an iOS environment, my memory was getting saturated by a leak, which affected both the RAM, and the disk (which it would fill up entirely in a few weeks of testing, all 64Gb). So I made a really simple downgraded version of it which showcases what exactly fails in it. In short, by calling the realm.open and realm.close methods, I leak memory (instances of realm_scheduler according to my memory graph on Xcode, but anything related to realms seems to create a way too large amount of instances in it).

Xcode memory illustration

In short, my Swift code to be run on iOS only executes this, which creates a timer calling leak() from an instance of RealmLeaker every .1s :

    var leaker = RealmLeaker()
    // Code skipped here for readability
        NavigationView {
            Text("A")
        }
        .onAppear() {
            var timer1 = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timerI in
                // Leaking
                leaker.leak()
            }
        }

In the KMM side, here's the RealmLeaker Class :

class RealmLeaker {
    fun leak() {
        val config = RealmConfiguration.Builder(setOf(TestClass::class))
            .build()

        // This should simply open, then close the realm. Note that if you comment everything below this, there is no leak.
        val realm: Realm = Realm.open(config)
        realm.close()
    }
}

And for more context, here's the "TestClass" that I use on the Kotlin side :

// This class isn't really useful. Only there to showcase the leak, its properties are outside the scope of the problem.
class TestClass : RealmObject{
    @PrimaryKey
    var _id : String = "0"

    var value : Boolean = false
    var key : String = "test"
}

Logically, this code should simply open and close the Realm, resulting in no loss of memory, yet it fills up my RAM and my disk memory.

Here’s a repo to reproduce the error : GitHub - ALXgba/realmKMMIssue: Memory leak using Kotlin API in a KMM project, when run in iOS environment. 1

I did open an issue on the MongoDB Realms GitHub page, but as they haven't answered yet I am hopeful that what I missed might be elementary and that I could possibly fix it in my code, as I wish to circumvent this issue quickly. Here's the issue page : https://github.com/realm/realm-kotlin/issues/1501

While the issue doesn't seem so terrible using the Xcode debug graphs, what isn't clearly shown by it is that the app's size on disk is also growing indefinitely, as said earlier. Meaning that a user WILL need to uninstall and reinstall periodically this app if they want to use it frequently, wiping the Realm in the process. Also, the RAM used seems to grow at a small rate, but I have observed it in my actual app (not the test one) growing fast enough to crash after an hour or so as the whole RAM gets clogged up.


Solution

  • For anyone experimenting this issue :

    There was an internal issue with Realms which in a nutshell involved cycle dependencies never being collected by the iOS equivalent of the GC. The issue has been closed by the following pull request : https://github.com/realm/realm-kotlin/pull/1530 It isn’t pushed in an actual release yet, but using the 1.11.2-SNAPSHOT linked with its current branch fixes the problem. (https://github.com/realm/realm-kotlin/tree/releases; see “Using Snapshots” in the README.md for more on that).

    Edit: It is now corrected from the stable release 1.12.0 and above.