0

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?

Progman
  • 16,827
  • 6
  • 33
  • 48
the_prole
  • 8,275
  • 16
  • 78
  • 163

2 Answers2

1

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.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Would you recommend using @Singlton bindings for a ROOM repository instance? – the_prole Aug 18 '22 at 20:33
  • I tend to think DI singletons are a "best of both worlds" for Room repositories or their wrappers/controllers, but you can see more details and answers here: [SO: Using Singleton within the Android Room Library](https://stackoverflow.com/q/50103232/1426891) – Jeff Bowman Aug 19 '22 at 02:09
0

Un-scoped bindings are not scoped to their components.

By default, all bindings in Hilt are unscoped. This means that each time your app requests the binding, Hilt creates a new instance of the needed type.

However, Hilt also allows a binding to be scoped to a particular component. Hilt only creates a scoped binding once per instance of the component that the binding is scoped to, and all requests for that binding share the same instance.

https://developer.android.com/training/dependency-injection/hilt-android#component-scopes

the_prole
  • 8,275
  • 16
  • 78
  • 163