Search code examples
javadagger-2daggerdagger-hilt

Should one install instance variable dependencies in the singleton module?


We can use the @Singleton annotation to denote the dependency as a singleton, and then install the dependency to the singleton component to give the singleton the application life time e.g.

@InstallIn(SingletonComponent::class)
class AppModule {

    @Provides @Singleton
    fun provideMyApi(): MyApi {
        return MyApiImpl()
}

but if we were to forget to add the @Singleton annotation, could this potentially cause a memory leak?

Because now we're adding a new instance variable with application life time every time we create the dependency?


Solution

  • It might lead to memory leaks, but only by compounding other problems. As a general statement, @Singleton objects can also leak memory.

    First and most obvious: If you meant to add @Singleton but forgot to do so, then every time you inject a MyApi, you would very likely get a different MyApiImpl instance. This might lead to incorrect behavior if you meant MyApiImpl to be stateful: you wouldn't want five counters with value = 1 instead of one counter with value = 5.

    Regarding memory, if MyApiImpl is injected a finite number of times--especially from @Singleton components--it might not be a "memory leak" in the traditional infinite sense: Your app would consume more memory than it might have otherwise, but the instances will likely be garbage-collected when their parent instances are garbage collected. You could probably find a way to create a memory leak by injecting lots of those instances via a Provider<MyApi> and saving them to an array or collection, but that's a problem of your usage, not of scopeless objects installed in Hilt Singleton components. Besides, instances without much state may take up only tens of bytes of memory in modern implementations; without careful metrics you might not even notice the difference.

    Done well, scopeless objects can have better memory behavior than scoped objects: If your injected object doesn't keep any state, then it is in your interest to destroy the instance and reclaim its memory as soon as you're done using it. Any object you request marked @Singleton will necessarily stick around for the lifetime of the application, so the runtime can never garbage collect the object or the objects it refers to. Arguably this too is not the fault of @Singleton, but rather one of usage; my point is just that neither mode is inherently better as long as you understand the semantics of the objects you inject.

    Aside: For stateless objects like strategy pattern objects (or some API surfaces), you might also consider @Reusable. It has some costs too, but allows some garbage collection and avoids synchronizing the object creation.