# Kotlin : Singleton with Memory Efficiency

Singletons are a fundamental design pattern in software development, ensuring that a class has only one instance and provides a global point of access to it. In Kotlin, implementing a singleton is straightforward using the `object` keyword, but what if we need a **memory-efficient, lazy-loaded, and thread-safe singleton**?

In this article, we'll explore different ways to implement a singleton in Kotlin while optimizing **memory usage** and **lazy initialization**. We'll compare eager vs. lazy instantiation, discuss potential pitfalls like unnecessary memory consumption, and implement a **double-checked locking singleton**, ensuring both efficiency and performance. Plus, we'll introduce a way to **destroy the singleton instance**, giving you more control over resource management.

By the end, you'll know which approach best suits your needs, whether you're building an Android app, a back-end service, or a high-performance Kotlin application.

Let's dive into the world of **memory-efficient singleton design in Kotlin** with some practical examples!

---

## Using Lazy Delegation

If we want a **lazy-loaded singleton**, Kotlin’s `lazy` delegation makes it incredibly easy:

```kotlin
class Singleton private constructor() {
    init {
        println("Singleton instance created")
    }

    companion object {
        val instance: Singleton by lazy { Singleton() }
    }

    fun doSomething() {
        println("Doing something...")
    }
}
```

✅ **Why use this?**

* The instance is created only when accessed for the first time.
    
* Thread-safe by default.
    

---

## Thread-Safe Lazy Singleton (Synchronized)

If we need a **thread-safe** singleton with explicit control, we can use `synchronized` to ensure only one instance is created, even in multi-threaded environments.

```kotlin
class SafeSingleton private constructor() {
    init {
        println("SafeSingleton instance created")
    }

    companion object {
        @Volatile
        private var instance: SafeSingleton? = null

        fun getInstance(): SafeSingleton {
            return instance ?: synchronized(this) {
                instance ?: SafeSingleton().also { instance = it }
            }
        }
    }
}
```

✅ **Why use this?**

* Thread-safe.
    
* Lazy initialization.
    
* Prevents multiple instances from being created in multi-threaded scenarios.
    

🔹 **Note:** `@Volatile` ensures that updates to `instance` are visible across threads. However, in some cases, `@Volatile` **might cause the object to load on the main thread** if accessed from the UI. While this is usually not an issue, we can manually handle initialization in a background thread if needed.

---

## Kotlin `object` Singleton (Best for Simplicity)

If we don’t need lazy initialization or additional control, Kotlin provides a built-in way to create singletons using the `object` keyword:

```kotlin
object SimpleSingleton {
    fun doSomething() {
        println("Doing something in SimpleSingleton")
    }
}
```

✅ **Why use this?**

* Short and simple.
    
* Thread-safe by default.
    
* Easy to use for lightweight singletons.
    

❌ **Downside?**

* **Eager initialization** (created at class load time, even if never used).
    

---

## Simple Lazy Singleton Without `synchronized`

If thread safety isn’t a concern, a **basic lazy singleton** without synchronization can be used:

```kotlin
class SimpleSingleton private constructor() {
    init {
        println("SimpleSingleton instance created")
    }

    companion object {
        private var instance: SimpleSingleton? = null

        fun getInstance(): SimpleSingleton {
            if (instance == null) {
                instance = SimpleSingleton()
            }
            return instance!!
        }
    }
}
```

✅ **Why use this?**

* Works fine in a single-threaded environment.
    
* Saves memory compared to an eager singleton.
    

❌ **Downside?**

* Not thread-safe.
    

---

## Optimized Singleton with Memory Efficiency & Manual Destruction

If we need a **fully optimized, thread-safe, and lazy-loaded singleton**, while also allowing **manual destruction**, the **double-checked locking singleton** is the best approach:

```kotlin
class OptimizedSingleton private constructor() {
    init {
        println("OptimizedSingleton instance created")
    }

    companion object {
        @Volatile
        private var instance: OptimizedSingleton? = null

        fun getInstance(): OptimizedSingleton {
            return instance ?: synchronized(this) {
                instance ?: OptimizedSingleton().also { instance = it }
            }
        }

        fun destroyInstance() {
            synchronized(this) {
                instance = null
                println("OptimizedSingleton instance destroyed")
            }
        }
    }

    fun doSomething() {
        println("Doing something in OptimizedSingleton")
    }
}
```

✅ **Why use this?**

* **Lazy initialization** (created only when needed).
    
* **Thread-safe** (using `synchronized`).
    
* **Efficient memory usage** (not pre-loaded).
    
* **Manual destruction** (`destroyInstance()`) allows explicit cleanup if needed.
    

---

## Comparing Singleton Approaches

| Approach | Lazy Initialization | Thread Safety | Memory Efficient | Manual Destruction | Performance |
| --- | --- | --- | --- | --- | --- |
| `object` Singleton | ❌ (Eager) | ✅ Yes | ❌ (Always Loaded) | ❌ No | ✅ Fast |
| Simple Companion Object | ✅ Yes (On Demand) | ❌ No | ✅ Yes | ❌ No | ✅ Fast |
| `Double-Checked Locking` | ✅ Yes (On Demand) | ✅ Yes | ✅ Best | ✅ Yes | ✅ Best |

---

## When to Use Which Singleton?

* **Use** `object` Singleton → When simplicity and quick access are preferred.
    
* **Use Simple Lazy Singleton** → If running in a single-threaded environment.
    
* **Use** `Double-Checked Locking` → When **memory efficiency, lazy initialization, and thread safety** are critical.
    
* **Use** `OptimizedSingleton` → If we need all the benefits of `Double-Checked Locking` **plus manual destruction**.
    

By choosing the right approach, we can optimize both memory usage and performance while keeping our singleton **efficient and scalable**.

---

That’s it for today. Happy Coding…

%%[buymeacoffee-donate]
