Setiap file .class yang dihasilkan kotlinc mengikuti format biner standar JVM Specification. Format ini bukan sekadar kumpulan instruksi mesin; file .class mengemas metadata lengkap tentang class, termasuk constant pool, daftar field, daftar method, dan atribut seperti Line Number Table yang dipakai untuk memetakan bytecode ke baris sumber. Pada chapter ini kita akan membongkar struktur file .class hasil kompilasi kode Kotlin sederhana, membaca instruksi bytecode utama, dan menggunakan javap -c untuk menampilkan isi class dalam bentuk yang dapat dibaca manusia.
Struktur File .class #
Spesifikasi JVM mendefinisikan struktur file .class sebagai urutan field berukuran tetap dan tabel variabel. Field pertama adalah magic number 0xCAFEBABE yang menjadi penanda bahwa berkas memang berformat class. Setelah magic number terdapat version (major dan minor), constant pool count, constant pool entries, access flags, this class, super class, interfaces, fields, methods, dan terakhir attributes.
Komponen yang paling sering kita jumpai saat membaca bytecode adalah:
| Komponen | Peran |
|---|---|
| Constant pool | Tabel string, angka, dan referensi simbol yang dipakai berulang oleh method |
| Access flags | Modifier public, final, abstract, static |
| Fields | Variabel instance dan static, dengan tipe descriptor |
| Methods | Instruksi bytecode + atribut Code (max stack, max locals, exception table) |
| Attributes | Metadata tambahan: SourceFile, LineNumberTable, LocalVariableTable |
Kode Sumber Sederhana #
Mari kita pakai kode berikut sebagai objek pengamatan. Kode ini sederhana namun mencakup pemanggilan method, string template, dan variabel lokal — tiga pola yang akan terlihat jelas di bytecode.
fun main() {
println("Halo, bytecode!")
val angka = 42
println("Angka favorit: $angka")
}Output yang Dihasilkan:
Halo, bytecode!
Angka favorit: 42Setelah kompilasi dengan kotlinc bytecode_demo.kt -d out/, kita memperoleh satu file Bytecode_demoKt.class (nama di-mangle dengan akhiran Kt karena fungsi top-level ditempatkan di kelas synthetic). File inilah yang akan kita periksa dengan javap.
Inspeksi dengan javap #
javap adalah alat bawaan JDK yang mencetak struktur class. Opsi -c menampilkan instruksi bytecode, -p menampilkan anggota privat, dan -v menampilkan seluruh atribut termasuk constant pool.
# Kompilasi tanpa -include-runtime agar hanya menghasilkan .class
kotlinc bytecode_demo.kt -d out/
# Lihat metode dan signature saja
javap out/Bytecode_demoKt.class
# Lihat instruksi bytecode
javap -c -p out/Bytecode_demoKt.class
# Lihat seluruh atribut termasuk constant pool
javap -v -p out/Bytecode_demoKt.classBerikut adalah cuplikan hasil javap -c -p pada method main (output disederhanakan untuk kejelasan):
public final static main()V
// string "Halo, bytecode!" dari constant pool #13
0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #15 // String "Halo, bytecode!"
5: invokevirtual #17 // Method java/io/PrintStream.println
// val angka = 42
8: iconst_42
9: istore_0
// println("Angka favorit: $angka") - dibangun StringBuilder
10: new #21 // class java/lang/StringBuilder
13: dup
14: ldc #23 // String "Angka favorit: "
16: invokevirtual #25 // StringBuilder.append
19: iload_0
20: invokevirtual #29 // StringBuilder.append:(I)
23: invokevirtual #32 // StringBuilder.toString
26: invokevirtual #17 // PrintStream.println
29: returnMari kita telaah baris-baris bytecode di atas:
getstatic #13: mengambil field statikSystem.outdari classjava/lang/System. Index#13merujuk pada entri constant pool yang berisi referensi simbolik ke field tersebut.ldc #15: memuat konstanta string"Halo, bytecode!"dari constant pool ke operand stack.ldcadalah singkatan load constant.invokevirtual #17: memanggil methodprintln(String)pada objekPrintStream.invokevirtualdipakai untuk method instance non-private.iconst_42laluistore_0: menyimpan nilai literal42ke variabel lokal index 0 (variabelangka). Instruksiiconst_<n>tersedia untuk angka-1sampai5; untuk42sebenarnya compiler memakaibipush 42, namun representasi logikanya sama.new StringBuilder+dup+append+toString: pola standar compiler untuk membangun string template. Setiap$angkadipanggil sebagaiappendpadaStringBuilder, lalu hasilnya di-println.
Constant Pool #
Constant pool adalah tabel semua literal dan referensi simbol yang dipakai dalam class. Setiap entri memiliki tag (CONSTANT_Utf8, CONSTANT_Integer, CONSTANT_String, CONSTANT_Methodref, dsb.) dan diakses melalui index numerik. Saat class dimuat, JVM resolve entri ini menjadi referensi langsung (direct reference) — proses ini disebut resolution.
Method dan Code Attribute #
Setiap method dalam class memiliki atribut Code yang menyimpan:
max_stack: kedalaman maksimum operand stackmax_locals: jumlah variabel lokal termasuk parametercode[]: array instruksi bytecodeexception_table[]: rentang instruksi yang dilindungitry/catchLineNumberTable: pemetaan instruksi → nomor baris sumber (dipakai oleh stack trace)LocalVariableTable: nama variabel lokal (dipakai oleh debugger)
Untuk function main di atas, max_locals = 1 (hanya variabel angka) dan max_stack = 2 (maksimum tiga nilai hidup bersamaan di stack saat membangun StringBuilder).
Referensi Lanjutan #
- JVM Specifications — spesifikasi resmi JVM Java SE 21 untuk struktur bytecode dan instruction set
- Kotlin/JVM — dokumentasi backend Kotlin/JVM yang menjelaskan penerjemahan kode ke bytecode
Penutup #
Memahami struktur .class memberi kita pijakan ketika membaca pesan galat linker, menelusuri masalah reflection, atau menulis custom transformasi bytecode dengan ASM. Pada chapter berikutnya, kita akan naik satu tingkat abstraksi dan melihat bagaimana JVM mengelola memori ketika bytecode dijalankan — mulai dari pemisahan stack dan heap.