Андрей Гузюк
Андрей Гузюк

Reputation: 2164

Singleton usage creates memory leak in kotlin

Trying to use Singleton class in another Singleton, but somehow it gives me memory leaks.

How could i improve it?

Here is my example singleton implementation

class FirstSingletonClass(val context: Context) {
    companion object {
        @Volatile
        private var instance: FirstSingletonClass? = null

        fun getInstance(context: Context): FirstSingletonClass =
                instance ?: synchronized(this) {
                    instance ?: FirstSingletonClass(context).also { instance = it }
                }

    }

   private val sSingletonClass: SecondSingletonClass = Injection.provideSecondSingletonClass(context)
}

SecondSingletonClass block

class SecondSingletonClass(val context: Context) {
    companion object {
        @Volatile
        private var instance: SecondSingletonClass? = null

        fun getInstance(context: Context): SecondSingletonClass =
                instance ?: synchronized(this) {
                    instance ?: SecondSingletonClass(context).also { instance = it }
                }

    }

   private val fSingletonClass: FirstSingletonClass = Injection.provideFirstSingletonClass(context)
}

Injection class

object Injection {
    fun provideSecondSingletonClass(context: Context): SecondSingletonClass = SecondSingletonClass.getInstance(context)
    fun provideFirstSingletonClass(context: Context): FirstSingletonClass = FirstSingletonClass.getInstance(context)
}

So when im initializing property - private val sSingletonClass or private val fSingletonClass it's produces memory leaks. But if i will call my singleton class somewhere in function block it works fine.

Is that possible to achieve what i want? Or should i use it explicitly...

Upvotes: 1

Views: 1815

Answers (2)

Son Truong
Son Truong

Reputation: 14173

Problem: You are passing the Context instance into the Singleton, the context might be an activity, a service, etc. It might make the context from leaking.

Solution: Using applicationContext instead.

class FirstSingletonClass(val context: Context) {
    companion object {
        @Volatile
        private var instance: FirstSingletonClass? = null

        fun getInstance(context: Context): FirstSingletonClass =
            instance ?: synchronized(this) {
                instance ?: FirstSingletonClass(context.applicationContext).also { instance = it }
            }
    }

    private val sSingletonClass: SecondSingletonClass = Injection.provideSecondSingletonClass(context)
}

class SecondSingletonClass(val context: Context) {
    companion object {
        @Volatile
        private var instance: SecondSingletonClass? = null

        fun getInstance(context: Context): SecondSingletonClass =
            instance ?: synchronized(this) {
                instance ?: SecondSingletonClass(context.applicationContext).also { instance = it }
            }

    }

    private val fSingletonClass: FirstSingletonClass = Injection.provideFirstSingletonClass(context)
}

Upvotes: 2

Rasmusob
Rasmusob

Reputation: 508

You are leaking a Context here. You should not hold a long-lived reference to a Context. You can instead have the Context as an argument in the methods of FirstSingletonClass and SecondSingletonClass that need them.

Android studio should give you this warning:

Do not place Android context classes in static fields (static reference to SecondSingletonClass which has field context pointing to Context); this is a memory leak (and also breaks Instant Run)

Your code can be simplified to this:

object FirstSingletonClass {
    private val sSingletonClass = SecondSingletonClass
}

object SecondSingletonClass {
    private val fSingletonClass = FirstSingletonClass
}

Upvotes: 2

Related Questions