Belajar Groovy - Performance & Optimization
Episode 16 of 23

Belajar Groovy - Performance & Optimization

Episode ini membahas optimasi performa Groovy: menilai trade-off antara dinamis dan statis, memakai @CompileStatic, memanfaatkan invoke dynamic, serta profiling JVM untuk menemukan bottleneck dan meminimalkan overhead runtime.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Groovy dipuji karena fleksibilitasnya, tetapi fleksibilitas memiliki harga: overhead runtime untuk dispatch dinamis. Episode 16 membahas cara mengukur, memahami, dan mengoptimalkan performa script Groovy.

Kalian akan belajar trade-off dinamis versus statis, memakai @CompileStatic, memahami peran invoke dynamic di JVM, serta profiling JVM untuk menemukan bottleneck dan meminimalkan overhead runtime.

Trade-Off Dinamis vs Statis

Biaya Dynamic Dispatch

Saat Groovy memanggil method secara dinamis, JVM tidak bisa langsung mengetahui method mana yang dieksekusi. Proses pencarian terjadi saat runtime melalui mekanisme seperti CallSite. Ini sangat fleksibel, tetapi menambah overhead dibandingkan pemanggilan statis Java.

Dua teknik utama yang mengurangi overhead:

  • Invoke dynamic (indy): strategi bawaan Groovy yang memakai instruksi JVM khusus, jauh lebih cepat dari call-site lama.
  • @CompileStatic: memaksa kompiler menghasilkan bytecode seperti Java, menghapus dynamic dispatch sepenuhnya.

Kapan Perlu Dioptimalkan

  • Script satu kali yang berjalan dalam hitungan detik tidak perlu dioptimalkan.
  • Kode di hot path — dipanggil jutaan kali — layak dioptimalkan.
  • Ukur dulu dengan profiling sebelum memutuskan.

Menggunakan @CompileStatic

Dasar Penggunaan

@CompileStatic memberitahu kompiler Groovy untuk melakukan type checking dan menghasilkan bytecode statis:

Method dengan @CompileStatic
import groovy.transform.CompileStatic
 
@CompileStatic
int totalNilai(List<Integer> nilai) {
    nilai.sum()
}
 
println totalNilai([10, 20, 30])

@CompileStatic pada method membuat kompiler memeriksa tipe List<Integer> dan nilai.sum() saat kompilasi. int totalNilai(List<Integer> nilai) menghasilkan bytecode yang setara dengan Java untuk method ini.

Level Penerapan

  • Method: type checking hanya untuk method tersebut.
  • Class: seluruh method di dalam class diperiksa statis.
  • Script: seluruh script diperiksa statis.

Semakin tinggi levelnya, semakin besar penghematan overhead, tetapi semakin sedikit fitur dinamis yang tersedia. Method yang memakai methodMissing atau metaprogramming tidak cocok dengan @CompileStatic.

Perbandingan Kinerja

Perbedaan performa bisa signifikan di hot path. Benchmark sederhana:

Benchmark dinamis vs statis
import groovy.transform.CompileStatic
 
@CompileStatic
long loopStatis() {
    long total = 0
    for (int i = 0; i < 1_000_000; i++) {
        total += i
    }
    total
}
 
long loopDinamis() {
    long total = 0
    for (i in 0..<1_000_000) {
        total += i
    }
    total
}
 
def waktu = { closure ->
    def mulai = System.nanoTime()
    closure()
    (System.nanoTime() - mulai) / 1e6
}
 
println "Statis: ${waktu { loopStatis() }} ms"
println "Dinamis: ${waktu { loopDinamis() }} ms"

def waktu = { closure -> ... } adalah closure pengukur waktu. (System.nanoTime() - mulai) / 1e6 menghitung durasi dalam milidetik. @CompileStatic long loopStatis() biasanya lebih cepat untuk loop seperti ini.

Meminimalkan Overhead Runtime

Hindari Objek Sementara

Salah satu overhead tersembunyi adalah pembuatan objek sementara:

  • Reuse collection yang sudah ada daripada membuat baru setiap iterasi.
  • Gunakan primitive int, long, dan double di method @CompileStatic.
  • Hindari interpolasi GString berlebihan di loop panas; gunakan StringBuilder.

GString dan StringBuilder

GString sangat nyaman, tetapi di hot path lebih cepat memakai StringBuilder:

StringBuilder untuk hot path
@CompileStatic
String bangunPesan(int n) {
    def sb = new StringBuilder()
    for (int i = 0; i < n; i++) {
        sb.append("baris ").append(i).append("\n")
    }
    sb.toString()
}
 
println bangunPesan(3)

sb.append("baris ").append(i) menggabungkan string tanpa membuat objek perantara. @CompileStatic String bangunPesan(int n) memastikan pemanggilan StringBuilder dikompilasi statis.

Profiling JVM

Mengukur dengan jcmd dan jconsole

Profiling dimulai dari pengukuran. JDK menyertakan tool bawaan:

Mengukur memory dengan jcmd
jcmd <pid> GC.heap_info
jconsole

jcmd <pid> GC.heap_info menampilkan kondisi heap proses JVM, dan jconsole membuka GUI monitoring real-time. Untuk profiling CPU yang lebih dalam, gunakan async-profiler atau Java Flight Recorder.

Menemukan Bottleneck

  • Ukur waktu eksekusi baseline sebelum mengubah apa pun.
  • Profiling CPU untuk menemukan method tersibuk.
  • Profiling memory untuk menemukan kebocoran atau objek berlebihan.
  • Optimalkan hanya area yang benar-benar dominan.

Membaca Heap Dump

Mengambil heap dump
jmap -dump:live,format=b,file=heap.hprof <pid>

jmap -dump:live,format=b,file=heap.hprof <pid> mengambil snapshot heap yang bisa dianalisis di VisualVM atau Eclipse MAT untuk menemukan objek yang tidak seharusnya bertahan.

Strategi Optimasi

Pendekatan Bertahap

Optimasi yang efektif bersifat bertahap:

  1. Profil dulu, jangan menebak-nebak.
  2. Terapkan @CompileStatic pada hot path.
  3. Kurangi pembuatan objek sementara.
  4. Ukur ulang setelah setiap perubahan.

Ingat aturan utamanya: kode yang benar dan mudah dibaca lebih berharga daripada kode yang paling cepat tapi tidak terpelihara.

Penutup

Episode 16 memberikan toolkit optimasi performa Groovy: memahami biaya dynamic dispatch, memakai @CompileStatic di hot path, menghindari objek sementara, serta profiling JVM dengan tool bawaan dan analisis heap dump.

Inti yang harus dibawa pulang:

  • Dynamic dispatch nyaman tetapi memiliki overhead runtime.
  • @CompileStatic menghasilkan bytecode statis ala Java.
  • Hot path layak dioptimalkan; script sekali jalan tidak.
  • Hindari GString dan objek sementara di loop panas.
  • Profiling CPU dan memory harus dilakukan sebelum optimasi.
  • Terapkan perubahan kecil dan ukur ulang setiap langkah.

Di episode 17 selanjutnya kita akan membahas web dan API development — menggunakan Groovy dengan framework seperti Grails atau Micronaut, menulis REST API sederhana dengan JSON serialization, serta deployment ke container, cloud, atau JVM service.

Belajar Groovy - Performance & Optimization | Belajar Groovy