Back to blog
September 11, 2025
4 min read

Performance Optimization in Kotlin

Learn techniques for optimizing Kotlin applications for better performance

Performance Optimization in Kotlin

Performance optimization is crucial for building efficient Kotlin applications. Let’s explore various techniques and best practices for optimizing Kotlin code.

Memory Management

Object Pooling

class ObjectPool<T>(
    private val factory: () -> T,
    private val maxSize: Int
) {
    private val pool = ConcurrentLinkedQueue<T>()

    fun acquire(): T {
        return pool.poll() ?: factory()
    }

    fun release(obj: T) {
        if (pool.size < maxSize) {
            pool.offer(obj)
        }
    }
}

// Usage
val stringPool = ObjectPool({ StringBuilder() }, 100)
val builder = stringPool.acquire()
try {
    builder.append("Hello")
} finally {
    stringPool.release(builder)
}

Memory Leak Prevention

class ResourceManager {
    private val resources = WeakHashMap<Any, Resource>()

    fun registerResource(key: Any, resource: Resource) {
        resources[key] = resource
    }

    fun cleanup() {
        resources.clear()
    }
}

Collection Optimization

Sequence Usage

// Good for large collections
val result = list.asSequence()
    .filter { it > 0 }
    .map { it * 2 }
    .toList()

// Avoid for small collections
val result = list
    .filter { it > 0 }
    .map { it * 2 }

Collection Initialization

// Good
val map = HashMap<String, Int>(expectedSize)

// Avoid
val map = HashMap<String, Int>()

Coroutine Optimization

Dispatcher Selection

// CPU-intensive work
withContext(Dispatchers.Default) {
    // Complex calculations
}

// IO operations
withContext(Dispatchers.IO) {
    // File operations
}

// UI updates
withContext(Dispatchers.Main) {
    // Update UI
}

Structured Concurrency

suspend fun processData() = coroutineScope {
    val result1 = async { processFirst() }
    val result2 = async { processSecond() }

    Result(result1.await(), result2.await())
}

Caching

In-Memory Cache

class Cache<K, V>(
    private val maxSize: Int
) {
    private val cache = LinkedHashMap<K, V>(maxSize, 0.75f, true)

    @Synchronized
    fun get(key: K): V? {
        return cache[key]
    }

    @Synchronized
    fun put(key: K, value: V) {
        if (cache.size >= maxSize) {
            cache.remove(cache.keys.first())
        }
        cache[key] = value
    }
}

Disk Cache

class DiskCache(
    private val directory: File,
    private val maxSize: Long
) {
    fun get(key: String): ByteArray? {
        val file = File(directory, key)
        return if (file.exists()) file.readBytes() else null
    }

    fun put(key: String, data: ByteArray) {
        val file = File(directory, key)
        file.writeBytes(data)
        cleanupIfNeeded()
    }

    private fun cleanupIfNeeded() {
        var totalSize = directory.walk()
            .filter { it.isFile }
            .map { it.length() }
            .sum()

        if (totalSize > maxSize) {
            directory.walk()
                .filter { it.isFile }
                .sortedBy { it.lastModified() }
                .forEach { file ->
                    if (totalSize <= maxSize) return@forEach
                    totalSize -= file.length()
                    file.delete()
                }
        }
    }
}

Image Processing

Bitmap Optimization

fun optimizeBitmap(bitmap: Bitmap, maxSize: Int): Bitmap {
    var width = bitmap.width
    var height = bitmap.height

    while (width * height > maxSize) {
        width /= 2
        height /= 2
    }

    return Bitmap.createScaledBitmap(bitmap, width, height, true)
}

Image Caching

class ImageCache {
    private val memoryCache = LruCache<String, Bitmap>(20)
    private val diskCache = DiskCache(File("cache/images"), 50 * 1024 * 1024)

    fun getImage(url: String): Bitmap? {
        return memoryCache.get(url) ?: diskCache.get(url)?.let {
            BitmapFactory.decodeByteArray(it, 0, it.size)
        }
    }
}

Network Optimization

Connection Pooling

class ConnectionPool(
    private val maxConnections: Int
) {
    private val pool = ArrayBlockingQueue<Connection>(maxConnections)

    fun getConnection(): Connection {
        return pool.poll() ?: createConnection()
    }

    fun releaseConnection(connection: Connection) {
        if (pool.size < maxConnections) {
            pool.offer(connection)
        } else {
            connection.close()
        }
    }
}

Request Batching

class BatchProcessor<T>(
    private val batchSize: Int,
    private val processor: (List<T>) -> Unit
) {
    private val batch = ArrayList<T>(batchSize)

    @Synchronized
    fun add(item: T) {
        batch.add(item)
        if (batch.size >= batchSize) {
            processBatch()
        }
    }

    private fun processBatch() {
        processor(batch.toList())
        batch.clear()
    }
}

Best Practices

Lazy Initialization

// Good
private val expensiveResource by lazy {
    createExpensiveResource()
}

// Avoid
private val expensiveResource = createExpensiveResource()

String Concatenation

// Good
val result = StringBuilder().apply {
    append("Hello")
    append(" ")
    append("World")
}.toString()

// Avoid
val result = "Hello" + " " + "World"

Performance Monitoring

Metrics Collection

class PerformanceMonitor {
    private val metrics = ConcurrentHashMap<String, Long>()

    fun measureTime(key: String, block: () -> Unit) {
        val startTime = System.nanoTime()
        block()
        val endTime = System.nanoTime()
        metrics[key] = endTime - startTime
    }

    fun getMetrics(): Map<String, Long> {
        return metrics.toMap()
    }
}

Memory Profiling

class MemoryProfiler {
    fun trackMemoryUsage() {
        val runtime = Runtime.getRuntime()
        val usedMemory = runtime.totalMemory() - runtime.freeMemory()
        val maxMemory = runtime.maxMemory()

        println("Used memory: ${usedMemory / 1024 / 1024}MB")
        println("Max memory: ${maxMemory / 1024 / 1024}MB")
    }
}

Conclusion

Performance optimization in Kotlin helps you:

  • Improve application speed
  • Reduce memory usage
  • Enhance user experience
  • Scale applications efficiently

Remember:

  • Use appropriate data structures
  • Implement caching strategies
  • Monitor performance metrics
  • Follow optimization best practices

This concludes our series on Kotlin programming! We hope you’ve found these posts helpful in your Kotlin journey.