Aplikasi Kotlin/JVM yang berjalan di produksi jarang memakai pengaturan default JVM. Sebagian besar beban kerja nyata memerlukan penyesuaian ukuran heap, pemilihan kolektor GC, dan pengaturan cadangan saat terjadi galat memori. Pengaturan ini diberikan lewat baris perintah java dalam bentuk flag yang diawali -X (ukuran) atau -XX (opsi extended). Pada chapter ini kita akan membahas flag yang paling sering dipakai, cara memverifikasinya di runtime, serta rekomendasi praktis untuk tipe aplikasi yang berbeda.

Kategori Flag #

Flag JVM dikelompokkan menjadi tiga kategori berdasarkan stabilitasnya:

KategoriPrefiksStabilitasContoh
Standard-Stabil lintas versi-cp, -jar, -Dprop=val
Non-standard-XStabil dalam satu vendor-Xmx, -Xms, -Xss
Developer-XXBisa berubah antar versi-XX:+UseG1GC, -XX:MaxGCPauseMillis=100

Flag -XX dapat berbentuk boolean (-XX:+Name / -XX:-Name) atau key-value (-XX:Key=Value).

Mengatur Ukuran Heap #

Dua flag yang paling sering dijumpai mengatur heap:

  • -Xms<size>: ukuran heap awal saat JVM start. Jika disetel tinggi, JVM tidak perlu tumbuh bertahap.
  • -Xmx<size>: ukuran heap maksimum. Melebihi batas ini memicu OutOfMemoryError: Java heap space.

Ukuran ditulis dengan suffix k, m, atau g (kilobyte, megabyte, gigabyte). Contoh: -Xmx2g berarti maksimum 2 gigabyte.

bash
# Heap awal 512 MB, maksimum 2 GB, dengan GC ZGC
java -Xms512m -Xmx2g -XX:+UseZGC -jar aplikasi.jar

# Heap besar untuk batch processing
java -Xmx8g -XX:+UseParallelGC -jar batch.jar

Memilih Garbage Collector #

Pilihan GC memengaruhi tradeoff throughput dan latensi. Empat kolektor utama yang dapat dipakai di JVM modern:

FlagKolektorKarakterCocok untuk
-XX:+UseSerialGCSerialSingle-thread, heap kecilCLI tools, container kecil
-XX:+UseParallelGCParallelThroughput-oriented, multi-threadBatch processing, ETL
-XX:+UseG1GCG1 (default JDK 9+)Region-based, target pauseServer umum, REST API
-XX:+UseZGCZGCSub-millisecond pauseLatency-critical, realtime
-XX:+UseShenandoahGCShenandoahConcurrent evacuationLatency-critical, heap besar

G1 menerima flag target pause -XX:MaxGCPauseMillis=<ms> (default 200). JVM akan menyesuaikan ukuran region untuk mendekati target, namun target bukan jaminan keras.

Heap Dump saat OOM #

Saat aplikasi kehabisan memori, logika bisnis biasanya rusak parah. Salah satu langkah pertama dalam investigasi adalah mendapatkan heap dump pada momen tepat sebelum proses berhenti. Flag berikut mengaktifkannya:

bash
java -Xmx1g \
     -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/var/log/app/heapdump.hprof \
     -jar aplikasi.jar

Saat OutOfMemoryError terpicu, JVM otomatis menulis heap dump ke path yang ditentukan. File .hprof kemudian dibuka dengan Eclipse MAT atau VisualVM untuk analisis retained size dan path to GC roots.

Inspeksi Flag Aktif di Runtime #

Kita dapat membaca properti sistem dan konfigurasi runtime lewat API Java. Kode berikut memperlihatkan properti JVM yang relevan dan daftar flag penting untuk referensi cepat.

kotlin
fun main() {
    println("=== Properti JVM yang Relevan untuk Konfigurasi ===")

    // Properti sistem dapat dibaca di runtime untuk verifikasi flag yang aktif
    listOf(
        "java.version" to "Versi Java",
        "java.vm.name" to "Nama JVM",
        "java.vm.info" to "Info JVM",
        "java.specification.version" to "Spec Java",
        "os.arch" to "Arsitektur OS",
        "file.encoding" to "Encoding default"
    ).forEach { (key, label) ->
        println("  $label ($key): ${System.getProperty(key)}")
    }

    println()
    println("=== Flag Penting untuk Aplikasi Produksi ===")
    println("  -Xms<size>           : ukuran heap awal (initial)")
    println("  -Xmx<size>           : ukuran heap maksimum")
    println("  -XX:+UseG1GC         : pakai kolektor G1 (default sejak JDK 9)")
    println("  -XX:+UseZGC          : pakai kolektor ZGC (low-latency)")
    println("  -XX:+UseShenandoahGC : pakai kolektor Shenandoah (concurrent)")
    println("  -XX:MaxGCPauseMillis : target pause time G1 (default 200 ms)")
    println("  -XX:+HeapDumpOnOutOfMemoryError : dump heap saat OOM")
    println("  -XX:HeapDumpPath=<path> : lokasi file heap dump")
    println("  -Xss<size>           : ukuran thread stack")
    println("  -XX:+UseStringDeduplication : dedup string di heap (G1)")

    println()
    println("=== Contoh Baris Perintah ===")
    println("  java -Xms512m -Xmx2g -XX:+UseZGC -jar app.jar")
    println("  java -Xmx4g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/dump.hprof -jar app.jar")
}

Output yang Dihasilkan:

text
=== Properti JVM yang Relevan untuk Konfigurasi ===
  Versi Java (java.version): 21.0.12.1
  Nama JVM (java.vm.name): OpenJDK 64-Bit Server VM
  Info JVM (java.vm.info): mixed mode, sharing
  Spec Java (java.specification.version): 21
  Arsitektur OS (os.arch): amd64
  Encoding default (file.encoding): UTF-8

=== Flag Penting untuk Aplikasi Produksi ===
  -Xms<size>           : ukuran heap awal (initial)
  -Xmx<size>           : ukuran heap maksimum
  -XX:+UseG1GC         : pakai kolektor G1 (default sejak JDK 9)
  -XX:+UseZGC          : pakai kolektor ZGC (low-latency)
  -XX:+UseShenandoahGC : pakai kolektor Shenandoah (concurrent)
  -XX:MaxGCPauseMillis : target pause time G1 (default 200 ms)
  -XX:+HeapDumpOnOutOfMemoryError : dump heap saat OOM
  -XX:HeapDumpPath=<path> : lokasi file heap dump
  -Xss<size>           : ukuran thread stack
  -XX:+UseStringDeduplication : dedup string di heap (G1)

=== Contoh Baris Perintah ===
  java -Xms512m -Xmx2g -XX:+UseZGC -jar app.jar
  java -Xmx4g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/dump.hprof -jar app.jar

Mari kita telaah kode di atas:

  1. System.getProperty(key): API standar Java yang membaca properti sistem yang ditetapkan saat startup (-Dprop=value) atau bawaan JVM. Kita memakainya untuk memverifikasi versi JVM dan encoding tanpa menjalankan java -version terpisah.
  2. Daftar properti: setiap pasangan (key, label) dicetak dengan format yang konsisten. Pola Pair + forEach ini menghindari duplikasi println untuk setiap properti.
  3. Daftar flag: ditampilkan sebagai cheat sheet di output program. Pembaca dapat membandingkan dengan daftar pada dokumentasi resmi Oracle atau OpenJDK untuk verifikasi.

Verifikasi Flag Aktif #

Selain membaca System.getProperty, JVM menyediakan dua mekanisme untuk memverifikasi flag mana yang aktif:

bash
# Menampilkan semua flag -XX dan nilainya
java -XX:+PrintFlagsFinal -version | head -20

# Untuk flag tertentu (sejak JDK 9)
jcmd <PID> VM.flags

# Mencatat GC flags ke stdout saat startup
java -Xlog:gc*=info -jar aplikasi.jar

Referensi Lanjutan #

  • JVM Guide — panduan resmi HotSpot JVM Java SE 21 untuk opsi -X, -XX, dan flags GC
  • Java SE 21 Thread — dokumentasi java.lang.Thread Java SE 21 untuk opsi ukuran stack thread

Penutup #

Pemilihan flag JVM yang tepat memengaruhi performa dan stabilitas aplikasi secara signifikan. Setel -Xms dan -Xmx sesuai profil memori, pilih kolektor GC berdasarkan tradeoff throughput/latensi, dan selalu aktifkan -XX:+HeapDumpOnOutOfMemoryError di produksi. Pada chapter berikutnya kita akan membahas alat profiling yang memanfaatkan konfigurasi ini untuk memberi visibilitas ke dalam runtime.