5 poin oleh GN⁺ 4 jam lalu | 2 komentar | Bagikan ke WhatsApp
  • JEP 401: Value Classes and Objects telah mencapai tahap masuk sebagai preview JDK yang nyata
  • Tujuan utamanya adalah membuat objek Java “dikodekan seperti class dan bekerja seperti int” sehingga mengurangi biaya header objek, alokasi heap, GC, dan indireksi pointer
  • Value class di JDK 28 masih berupa tipe referensi yang bisa bernilai null; tipe non-null, generic terspesialisasi, dan encoding 128-bit belum disertakan, serta memerlukan --enable-preview
  • JVM dapat menskalarkan value object atau melakukan perataan heap pada field dan array, tetapi pada tipe induk seperti erased generic atau Object, objek bisa dimaterialisasi sebagai objek heap
  • Pengembang Java perlu mencerminkan perbedaan antara identity dan value dalam desain kode, dan dampaknya meluas ke ==, synchronized, primitive wrapper, performa array, hingga spesialisasi generic di masa depan

Cakupan Valhalla yang masuk ke JDK 28

  • Pada 15 Juni, insinyur Oracle Lois Foltan mengonfirmasi integrasi JEP 401: Value Classes and Objects ke repositori utama OpenJDK dan target JDK 28
  • Pull request terkait menambahkan lebih dari 197 ribu baris di 1.816 file
  • Karena skala perubahannya besar, saat integrasi sempat ada permintaan kepada committer lain untuk menahan sementara commit besar
  • JEP 401 adalah fitur preview yang dinonaktifkan secara default
    • Untuk memakai sintaksnya, diperlukan --enable-preview
    • Brian Goetz menegaskan ini sebagai “bagian pertama dari Valhalla”
  • JDK 28 dijadwalkan rilis pada Maret 2027, dan integrasi ke mainline direncanakan sekitar Juli 2026

Biaya model objek Java yang dibidik Valhalla

  • Slogan Valhalla adalah “codes like a class, works like an int
    • Tujuannya adalah tetap memakai class biasa dengan method, validasi konstruktor, dan nama field yang bermakna, sambil memungkinkan JVM menanganinya seefisien primitive
  • Di Java, selain 8 primitive, hampir semua adalah tipe referensi
    • Pada Point p = new Point(1, 2), p bukan point itu sendiri melainkan pointer yang menunjuk ke objek di heap
    • Setiap kali field dibaca, JVM harus mengikuti pointer tersebut
  • Saat jumlah objek bertambah, biayanya meningkat tajam
    • Setiap objek memiliki header objek untuk tipe, status sinkronisasi, dan sebagainya
    • Objek dialokasikan di heap dan kemudian menjadi target GC
    • Array berisi satu juta Point pada kenyataannya terdiri dari satu juta pointer dan satu juta objek yang tersebar di seluruh heap
  • “State of Valhalla” dari Brian Goetz menyebut tata letak memori seperti ini sebagai fluffy
    • Yang diinginkan Valhalla adalah tata letak dense di mana data tersusun berdampingan

Kesenjangan perangkat keras dan batas escape analysis

  • Alasan tata letak memori yang padat penting adalah kesenjangan kecepatan antara CPU dan memori
    • Pada 1995, biaya akses memori mirip dengan operasi CPU
    • Kini CPU puluhan kali lebih cepat daripada memori utama, dan cache menutup kesenjangan ini
  • CPU biasanya membaca memori dalam unit cache line 64-byte
    • Jika data padat dan tersusun berurutan, banyak nilai berguna bisa diambil sekaligus
    • Jika harus mengikuti pointer ke objek yang tersebar, cache miss dapat terjadi dan bisa jauh lebih lambat daripada hit
  • Escape analysis di JVM dapat menghilangkan sebagian alokasi objek
    • Jika objek dinilai tidak “escape” ke luar bagian kode lokal, objek tidak dialokasikan di heap dan field-nya bisa diurai menjadi variabel atau register
  • Namun escape analysis kurang dapat diprediksi dan rapuh
    • Jika objek masuk ke field class lain, disimpan dalam array, diteruskan ke method yang kompleks, atau melintasi batas yang tak bisa dianalisis JIT, optimisasi bisa berhenti
    • Refactoring kecil, pembaruan JDK, atau perubahan struktur kode saja dapat membuat objek kembali dialokasikan ke heap
  • Jika demi performa orang meninggalkan objek dan mengenkode langsung sebagai byte mentah seperti r, g, b, kecepatan memang bisa didapat, tetapi keamanan, keterbacaan, validasi, dan method akan hilang

Awal pada 2014 dan peralihan dari Q World ke L World

  • Project Valhalla secara resmi dimulai pada 2014
  • James Gosling saat itu menggambarkannya sebagai “six PhDs tied into a single knot”
  • Para pencipta Java sudah menginginkan value type sejak era Java 1.0, tetapi pada 1995 masalahnya terlalu sulit sehingga ditinggalkan
  • Tujuan awalnya adalah memulihkan keselarasan antara model pemrograman dan karakteristik performa perangkat keras modern
    • Arah yang dituju adalah memungkinkan pengguna mendeklarasikan tipe yang flat dan dense seperti primitive, tetapi tetap terlihat dan berperilaku seperti class biasa
  • Prototype awal mengarah ke Q World
    • Value type baru dipandang sebagai entitas yang secara fundamental berbeda dari objek, dengan type descriptor, bytecode, dan top type tersendiri
    • Seluruh sistem tipe JVM jadi harus memiliki dua variasi, sehingga kompleksitas meningkat
  • L World yang muncul sekitar 2019 menjadi titik balik
    • Value type berbagi “L carrier” yang sama dengan referensi biasa
    • Tim memperkirakan integrasi ini akan sulit, tetapi ternyata berjalan tanpa kompromi besar dan menyelesaikan berbagai masalah dari prototype sebelumnya
  • Di L World, muncul pemisahan penting
    • Model JVM dan model bahasa tidak harus tumpang tindih 100%
    • JVM dapat memakai model L World, sementara programmer diberi model bahasa yang lebih nyaman
  • Setelah itu, pekerjaan dibagi menjadi dua tahap: value class dan generic terspesialisasi

Perubahan nama dan model

  • Istilah Valhalla berubah beberapa kali, dan itu bukan sekadar pergantian nama, melainkan mencerminkan perubahan model
  • Istilah awalnya adalah value types
    • Saat itu, belum sepenuhnya jelas tipe ini sebenarnya seperti apa
  • Sekitar 2019~2020, model inline classes mulai terbentuk
    • Kelas yang sudah ada diperlakukan sebagai identity classes, sedangkan kelas baru dibedakan sebagai inline classes tanpa identity
    • inline class pada dasarnya final, field-nya final, dan ada batasan bahwa ia tidak bisa disinkronkan
  • “State of Valhalla” tahun 2021 membahas primitive classes dan model dua projection
    • Gagasannya adalah satu tipe memiliki value variant yang flat dan tidak bisa null, serta reference variant yang mengizinkan null
    • Sintaks seperti Point.val / Point.ref, lalu Point! / Point?, juga pernah diuji
  • Model ini kuat, tetapi memiliki beban kognitif yang besar
    • Programmer harus memahami dua bentuk dari tipe yang sama dan kapan konversi terjadi dalam penggunaan sehari-hari
    • Pada akhirnya, dualisme itu diperkecil untuk menyederhanakan model bagi pengguna
  • Saat ini JEP 401 menggunakan value class dan value object
    • value class dideklarasikan dengan modifier value
    • Instance-nya adalah value object tanpa identity
    • value class tetap merupakan reference type
  • Non-nullability dipisahkan ke JEP opsional tersendiri, Null-Restricted Value Class Types
    • Ini tidak termasuk dalam JDK 28
  • Artikel lama yang menjelaskan model “primitive classes” sebelumnya bisa jadi berbeda dari standar OpenJDK saat ini
  • JEP 401 juga hadir bersama preview JEP 402: Enhanced Primitive Boxing
    • Arahannya adalah membuat konversi antara primitive dan wrapper lebih mulus
    • Jangan berasumsi semuanya akan masuk bersama JEP 401 dalam bentuk yang sudah final

Model value class di JDK 28

  • value class dideklarasikan dengan modifier value
value class USDCurrency implements Comparable<USDCurrency> {  
    private int cents; // implicitly final  
    public USDCurrency(int dollars, int cents) {  
        this.cents = dollars * 100 + cents;  
    }  
  
    public USDCurrency plus(USDCurrency that) {  
        return new USDCurrency(0, this.cents + that.cents);  
    }  
  
    // dollars(), cents(), compareTo(), toString()...  
}  
  • value record juga dimungkinkan
  • Aturan utamanya sebagai berikut
    • Semua instance field bersifat implicitly final
    • method tidak boleh synchronized
    • kelas secara default bersifat final
    • hierarki yang terdiri dari value class dan abstract value class dimungkinkan
    • tidak bisa mewarisi kelas yang memiliki identity
    • implementasi interface diperbolehkan
  • Sifat intinya adalah tidak memiliki identity
    • Objek biasa, meskipun isinya sama, jika dibuat dua kali dengan new Point(1, 2) tetap merupakan dua objek berbeda
    • value object tidak memiliki identity, sama seperti pada nilai int 4 tidak ada “dua angka 4 yang berbeda”

Perubahan pada ==, synchronized, dan null

  • Pada value object, == bukan perbandingan identity, melainkan pemeriksaan substitutability
    • Ia membandingkan secara rekursif apakah kelasnya sama dan nilai field-nya sama
    • field primitive dibandingkan per bit, dan field object dibandingkan lagi dengan ==
    • new USDCurrency(3,95) == new USDCurrency(3,95) akan bernilai true
  • Namun karena == melihat state internal, untuk pertanyaan “apakah merepresentasikan data yang sama”, equals biasanya lebih tepat
  • value object tidak memiliki identity yang bisa dipakai untuk sinkronisasi
    • Jika sinkronisasi dicoba, akan muncul IdentityException
    • Jika perlu memastikan identity secara eksplisit, bisa menggunakan Objects.requireIdentity dan Objects.hasIdentity
  • value class di JDK 28 tetap bisa bernilai null
    • USDCurrency d = null; adalah sah
    • Tipe yang melarang null akan hadir lewat JEP terpisah di masa depan, dan belum ada di JDK 28
  • Non-nullability bukan sekadar soal sintaks, tetapi juga menjadi tuas performa yang membuka perataan value class yang lebih besar

Skalarisasi dan perataan heap

  • JEP 401 memberi JVM kebebasan untuk mengoptimalkan value object
  • Skalarisasi (scalarization) adalah teknik JIT yang memecah reference value object menjadi sekumpulan field
    • Alih-alih mengoper pointer Color, JVM bisa mengoper byte r, g, b dan flag apakah null
    • Biaya alokasi dan GC bisa hilang
    • Ini mirip escape analysis, tetapi lebih dapat diprediksi, dan bisa diterapkan melewati batas pemanggilan method yang tidak di-inline
  • Skalarisasi memiliki keterbatasan
    • Jika tipe variabel adalah supertype value class seperti Object atau parameter generic yang sudah di-erasure, biasanya ini tidak bekerja
    • Dalam kasus seperti itu, objek harus dimaterialisasi di heap
  • Perataan heap (heap flattening) adalah cara menulis nilai field dari value object langsung ke field atau sel array dengan mengenkodenya sebagai bit vector yang ringkas
    • Tidak perlu pointer yang menunjuk ke lokasi heap lain
    • Ini menghasilkan kepadatan data dan locality
  • Data yang sudah diratakan harus bisa dibaca dan ditulis secara atomic untuk menghindari tearing saat akses konkuren
    • Pada platform umum, ukuran yang “cukup kecil” bisa berada di kisaran 64-bit termasuk flag null
    • value class kecil mungkin bisa diratakan dengan baik, tetapi bahkan dua field int atau satu double saja bisa tidak cocok dengan ukuran atomic write sehingga menjadi objek heap biasa
  • Ke depan, encoding 128-bit dan null-restricted type dapat memungkinkan perataan value class yang lebih besar

Dampak pada boxing, wrapper, dan array

  • Jika preview diaktifkan, primitive wrapper class seperti Integer, Long, dan Double sendiri akan menjadi value class
    • Karena box kehilangan identity, JVM dapat melakukan skalarisasi dan perataan
    • Integer[] akan bergerak mendekati efisiensi int[], dan overhead boxing akan berkurang besar
  • JEP 402: Enhanced Primitive Boxing memperluas konversi antara primitive dan box
    • Ini membuka jalan menuju ekspresi seperti List<int>, tetapi masih merupakan pekerjaan terpisah yang sedang dimatangkan
  • Efeknya paling jelas terlihat pada array
    • Color[] pada model lama bisa menjadi sejuta pointer dan sejuta objek yang tersebar di heap
    • value class Color[] bisa menjadi contiguous block yang langsung menyimpan nilai warna secara berurutan
    • CPU dapat membaca banyak nilai secara berurutan per cache line

Perbedaan sebelum dan sesudah lewat contoh Point[]

  • Contoh class biasa sebelum Valhalla adalah sebagai berikut
final class Point {  
    final int x;  
    final int y;  
    Point(int x, int y) { this.x = x; this.y = y; }  
}  
  
Point[] points = new Point[1_000_000];  
  • Array ini menampung satu juta pointer
    • Setiap pointer menunjuk ke objek Point terpisah di suatu lokasi di heap
    • Setiap objek memiliki header objek selain dua int
    • Saat diiterasi, pointer harus dibaca, lalu lompat ke alamat tersebut, kemudian membaca field-nya
  • Setelah Valhalla, contoh value class menjadi seperti berikut
value class Point {  
    final int x;  
    final int y;  
    Point(int x, int y) { this.x = x; this.y = y; }  
}  
  
Point[] points = new Point[1_000_000];  
  • Perbedaan pada kode hanya satu kata, yaitu value, tetapi tata letak memorinya berubah
    • JVM dapat menyimpan nilai setiap point secara rapat langsung di dalam array
    • Tidak ada header maupun pointer untuk setiap elemen
    • Berdasarkan dua int x, y, data dapat disusun berurutan sebagai 8 byte ditambah kemungkinan null flag
  • Maintainability juga tetap terjaga
    • Point tetap merupakan class yang memiliki nama, konstruktor, validasi, dan method
    • Anda bisa menghindari pendekatan memecahnya menjadi int[] xs, int[] ys lalu mencocokkan indeksnya

Mengapa specialized generics masih tersisa

  • Java generics diimplementasikan dengan type erasure
    • List<String> dan List<Integer> adalah List yang sama saat runtime
    • type parameter T di-erasure menjadi Object
  • Erasure adalah pilihan yang disengaja agar generics bisa diperkenalkan tanpa merusak codebase Java yang sudah ada
    • Bahkan jika class non-generic diubah menjadi generic, source file dan compiled class yang lama tetap tidak rusak
  • Valhalla dan erasure bertabrakan dari sisi performa
    • Jika value object dimasukkan ke List<Point>, T akan di-erasure menjadi Object, sehingga objek itu harus dimaterialisasi di heap
    • Keuntungan flattening yang didapat dari Point[] bisa hilang di ArrayList<Point>
  • Rencana pemulihannya terdiri dari dua tahap
    • Universal Generics: pada level bahasa, type variable akan bisa menangani value type juga
      • Tetap masih memakai erasure
      • Karena field T secara default dimulai dari null, bisa muncul compiler warning “null pollution”
      • Jika warning itu diselesaikan, API akan menjadi lebih dekat ke kondisi specialization-ready
    • Specialized Generics: pada level JVM, akan dibuat layout class yang terspesialisasi untuk tiap concrete type argument
      • Dalam istilah proyek, species dan type restriction berkaitan dengan tahap ini
      • Baru pada tahap inilah ArrayList<Point> bisa benar-benar memakai memori flat
  • JDK 28 belum memiliki full specialized generics
    • Koleksi, stream, dan API yang menjadi flat serta bebas alokasi di atas value type masih merupakan pekerjaan untuk rilis mendatang

Yang ada dan yang tidak ada di JDK 28

  • Yang masuk ke JDK 28 adalah sebagai berikut
    • deklarasi value class dan value record
    • migrasi ke value class untuk sebagian value-based class yang sudah ada di JDK, termasuk class seperti primitive wrapper
    • skalarisasi dan flattening untuk class yang memenuhi syarat
    • boxing yang lebih murah
  • Yang belum ada di JDK 28 adalah sebagai berikut
    • null-restricted types
    • full specialized generics
    • encoding 128-bit
    • JEP 402 yang benar-benar matang
  • Karena ini adalah preview feature, syntax dan perilakunya bisa berubah di setiap rilis sesuai feedback
  • JDK 28 bukan LTS
    • LTS berikutnya kemungkinan besar adalah JDK 29 pada September 2027
    • Banyak perusahaan mungkin akan bertemu Valhalla yang sudah stabil di LTS, tetapi preview JDK 28 memulai feedback loop dari kode nyata

Perubahan yang akan terjadi pada ekosistem dan kode

  • Di ranah Java performa tinggi, Valhalla menjadi jalur untuk menangani data padat tanpa mengorbankan abstraction
    • Ini mencakup area seperti pemrosesan data, komputasi vektor, ML, pengembangan game, keuangan, dan codec
  • Framework dan library dapat mulai melakukan migrasi value-based class
  • Kode yang bergantung pada identity bisa mengalami perbedaan perilaku
    • == pada value object tidak lagi berarti perbandingan alamat, melainkan perbandingan substitutability
    • synchronized pada value object akan berujung pada IdentityException
  • Walaupun Integer menjadi value class, pada kebanyakan kasus binary tetap akan terus terhubung
    • Compilation error baru muncul jika mencoba melakukan sinkronisasi pada tipe seperti ini
    • == yang bergantung pada identity Integer atau synchronized(someInteger) bisa terdampak
  • Build early-access tersedia di jdk.java.net/valhalla

Ringkasan pertanyaan yang sering muncul

  • value class berbeda dari record
    • record adalah pilihan bahwa content dinyatakan sebagai component
    • value adalah pilihan untuk melepaskan identity
    • Kombinasi class biasa, record, value class, dan value record semuanya dimungkinkan
  • value object bisa dibandingkan dengan ==
    • Maknanya bukan perbandingan alamat, melainkan substitutability
    • Untuk kesetaraan data yang direpresentasikan, biasanya equals lebih tepat
  • value class di JDK 28 masih bisa bernilai null
    • non-nullable type adalah JEP masa depan
    • Ini juga penting untuk flattening pada value class yang lebih besar
  • ArrayList<Point> flat yang cepat masih belum ada
    • Karena type erasure, objek di dalam generic collection harus dimaterialisasi di heap
    • Di JDK 28, contoh utama flattening yang bekerja langsung adalah field dari value type dan array seperti Point[]
  • escape analysis tidak bisa menggantikan semuanya
    • Jika objek keluar ke field, array, atau melewati batas analisis, optimisasi bisa gagal
    • Skalarisasi value object lebih dapat diprediksi dan bisa melampaui batas method call lebih jauh
  • Valhalla secara keseluruhan akan berkembang lintas beberapa rilis
    • JDK 28 adalah preview pertama untuk value class
    • specialized generics, null-restricted types, dan encoding 128-bit adalah pekerjaan yang tersebar ke rilis-rilis mendatang

2 komentar

 
click 1 jam lalu

Virtual thread yang dirilis setelah waktu yang lama melalui Project Loom itu praktis dan menyelesaikan banyak hal di level runtime JVM, jadi beban yang perlu dipikirkan developer relatif lebih sedikit.
Semoga Project Valhalla juga saat rilis final nanti terasa mendekati “makan siang gratis” seperti itu.

 
GN⁺ 4 jam lalu
Komentar Hacker News
  • Katanya perbedaan memori itu hal yang mendasar, tapi saya jadi ragu apakah tulisan ini benar-benar direview dengan baik
    Bukankah barusan dijelaskan bahwa objek dengan representasi lebih dari 64 bit tidak bisa di-flatten di heap? Contoh Point punya dua bilangan bulat 32 bit plus flag null, jadi minimal 65 bit
    Melihat ungkapan seperti “mungkin ada flag null” dan kalimat-kalimat penegasan pendek setelahnya, rasanya seperti AI sedang membuat kalimat penekanan lalu malah keluar jalur, dan blok "[IMAGE: the same Point[] array in two variants..." di tengah itu juga disayangkan

    • Kalimat seperti “Tidak ada header per elemen. Tidak ada pointer. Tidak melompat-lompat ke heap.” berbau gaya AI, jadi terlihat seperti tulisan yang malas
      Tidak masalah memakai AI untuk membantu menulis, tapi kalau tidak memasukkan suara sendiri, tidak ada alasan untuk membacanya
      https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing#...
    • Topiknya terlihat sangat menarik jadi saya ingin membacanya, dan bahkan gambar buatan AI masih bisa saya toleransi
      Tapi setelah beberapa paragraf, jadi jelas bahwa ini tulisan yang melewati LLM atau dibuat dengan cara yang lebih buruk lagi
      Baik blog teknis atau apa pun, tolong jangan biarkan AI yang menulis menggantikan manusia. Tidak ada yang ingin membaca tulisan seperti itu
    • Ada sebanyak 18446744073709551616 nilai yang mungkin, masa tidak bisa menyisihkan 1 di antaranya untuk null? :)
      Hari ini saya jadi tahu bahwa Rust punya NonZeroU64, dan bila digabungkan dengan Optional, kita bisa mendapatkan perilaku yang dibutuhkan dengan hanya 64 bit per entri
      https://doc.rust-lang.org/std/num/type.NonZeroU64.html
    • Terlalu jelas bahwa AI dipakai berlebihan, jadi saya berhenti setelah membaca 2 paragraf
    • “Objek dengan representasi lebih dari 64 bit tidak bisa di-flatten di heap” itu adalah cerita dari commit awal
      Seperti yang juga jelas di JEP, ini hanyalah hasil pertama dari fitur yang sangat besar, dan seperti fitur-fitur Java belakangan ini, pengirimannya dilakukan sedikit demi sedikit
      Tentu saja tujuannya adalah mem-flatten nilai yang lebih besar juga, dan mekanismenya sudah ada di dalam JVM. Yang tersisa hanyalah mengekspresikan di level bahasa bahwa “tearing diperbolehkan”
  • Saya mengakui besarnya upaya yang benar-benar masuk ke Valhalla, tapi saya sulit setuju dengan tafsiran bahwa “modelnya kuat tetapi secara mental terasa berat”
    Mengatakan bahwa sebuah variabel tidak bisa bernilai null bukanlah pembedaan yang memberatkan secara mental, apalagi jika semuanya ditandai dengan cukup jelas
    Sikap “menyederhanakan model pengguna meski harus mengorbankan batas atas performa” justru bisa jadi memang memberikan penyederhanaan bagi pengguna
    Sistem tipe bahasa pemrograman ada untuk memberi jaminan yang nyaman bagi pengembang di atas CPU yang pada dasarnya hanya bisa memproses angka. Tidak perlu mengurangi jaminan keamanan opsional hanya karena dianggap “terlalu rumit”
    Bahkan setelah sampai pada pemahaman bahwa “model bahasa dan model JVM tidak harus 100% tumpang tindih” pun tetap begitu

    • Saya tidak terlalu yakin Java bisa menentukan arah yang tepat
      Tata kelola Java terlihat kurang memadai, dan ini sangat kontras terutama dengan .NET yang sejak awal umumnya mengambil keputusan yang benar
      Belakangan saya juga bertanya-tanya apakah Java masih punya nilai atau perhatian di dalam Oracle. Sekarang perusahaan itu terlihat seperti bisnis data center/komputasi dengan aktivitas warisan dan utang besar yang menempel
      Kadang saya merasa satu-satunya bagian Oracle yang masih benar-benar menghasilkan uang hanyalah tim legal dan divisi pemotong rumput
    • Bukankah keluhan itu ditujukan ke blogger-nya, bukan ke bahasa Java?
      Dan penanda null juga akan datang: https://openjdk.org/jeps/8303099
      Hanya saja memang harus dirilis bertahap, dan PR untuk memperkenalkan value class/object ini saja sudah berukuran 200 ribu baris
    • Value type yang tidak bisa null memang hanya dijadwalkan dibahas di JEP lanjutan
      Sepertinya bukan berarti itu mustahil, hanya saja Anda tidak bisa memakan seekor gajah dalam sekali suap
      Meski begitu, satu kaki ini memang sudah dikunyah cukup lama
    • nullable hanyalah keadaan muatan lain dalam railway-oriented programming
      Jika ini konsep yang sudah terselesaikan sejak 2012, tidak ada alasan untuk memasukkan berbagai rasa state langsung ke dalam bahasa. Rel hanya menuju A atau B, dan yang menentukan jalurnya adalah keadaan muatan keretanya
      Kalau suatu konsep terus muncul lalu menghilang hingga memicu perang bahasa, itu tanda bahwa ada kebutuhan yang tidak ditangani dengan baik oleh bahasa tersebut, atau ditangani dengan cara yang menciptakan beban mental
      Hal-hal seperti Value, Errorstates, Null, IoExceptions, WeirdOsStatesNeededToHandleUpstairs
      https://fsharpforfunandprofit.com/rop/
      Meminjam gaya Monty Python, sekarang mari kita lanjut saja
    • Yang dibicarakan di sini bukan keamanan null, melainkan proyeksi referensi/nilai yang mirip Integer/int
      Tim Valhalla, alih-alih memberi setiap tipe proyeksi yang punya identitas dan yang tidak, membuat value type sama sekali tidak memiliki identitas, sehingga Integer dan int menjadi sinonim
      Tata letak memori lalu ditentukan otomatis berdasarkan konteks dan keputusan optimisasi. Karena itu makna == untuk wrapper primitif seperti Integer juga berubah, dan sekarang tidak lagi bergantung pada apakah kita memakai “proyeksi referensi” atau “proyeksi nilai”
      Tidak ada pengurangan jaminan keamanan opsional dengan alasan “memberatkan secara mental” yang terjadi di sini
  • Dalam komentar HN terkait Java/JVM, hal yang berulang terlihat adalah betapa mengejutkannya banyak orang masih punya citra lama tentang JVM atau Java, tetapi hampir tidak tahu wujudnya saat ini
    JVM tahun 2026 adalah predator yang sangat sehat. Apakah ada kekurangannya? Tentu saja, tetapi fondasinya luar biasa bagus

    • Di HN sulit mendapatkan penilaian bagus untuk JVM, dan di sini ia diperlakukan seperti teknologi yang sudah lewat masa jayanya
      Di pekerjaan saya memakai Java 26 terbaru dan fitur pratinjau, terutama StructuredConcurrency, dan itu luar biasa. Bahkan dari sudut pandang seseorang yang di perusahaan-perusahaan sebelumnya memakai Haskell dan Python, saya sama sekali tidak menyesal
    • Banyak orang masih harus memelihara monolit Java yang dimulai pada masa Java populer di tahun 2000-an, dan sampai sekarang tetap harus menjalankannya di Java 8
      Secara pribadi saya tahu fitur-fitur baru yang keluar dalam beberapa tahun terakhir, tetapi di pekerjaan nyata Java secara harfiah masih terjebak di masa lalu
  • Banyak komentar di sini agak tidak adil dibanding pekerjaan hebat yang sedang berlangsung sekarang dan JEP yang akan datang yang bahkan lebih keren
    Kalau Java diibaratkan anak, beberapa tahun pertamanya dibesarkan oleh orang tua penuh kasih (Sun), lalu setelah itu dilempar ke garasi bersama anak-anak lain dan ditelantarkan oleh wali yang jahat (Oracle)
    Karena ditelantarkan dan tidak dicintai sampai JDK 8, pada dasarnya Java selama ini sedang mengejar ketertinggalan
    Pernyataan seperti “baru sekarang ada struct atau value type” memang benar, tetapi itu karena pertumbuhannya terhambat oleh proses korporat yang besar, birokratis, dan bermusuhan. Sekarang Java sudah bebas dan dicintai melalui keluarga OpenJDK
    Ke depan kita akan terus menikmati kegembiraan tulis sekali, distribusikan ke mana saja

    • Suka atau tidak suka Oracle, itu bukan gambaran yang benar tentang sejarah Java
      Lebih tepatnya, ia dibesarkan oleh orang tua penuh kasih, lalu karena masalah keuangan dititipkan ke keluarga asuh dan di sanalah ia ditelantarkan
      Setelah itu ia diadopsi oleh orang tua baru yang penuh kasih, yaitu Oracle, dan Java pun berkembang menjadi orang dewasa yang sehat dan stabil
      Oracle juga yang menjadikan OpenJDK implementasi referensi dan menyelesaikan open-source platform tersebut, serta meng-open-source alat yang sebelumnya tertutup seperti JFR dan Mission Control
      Mereka juga mempertahankan banyak anggota asli tim bahasa, sesuatu yang cukup jarang dalam akuisisi seperti ini, dan Java meningkat besar baik di sisi bahasa maupun runtime
    • Java ditelantarkan pada beberapa tahun terakhir Sun
      Oracle mendorong Java maju dengan kecepatan yang belum pernah ada sebelumnya sambil tetap mempertahankan sebagian besar kompatibilitas ke belakang
      .NET disebut “melakukannya dengan benar dari awal”, tetapi kalau yang dimaksud adalah pemisahan dan penulisan ulang .NET Framework/.NET Core/.NET, itu pun tidak masuk akal bahkan dalam diskusi ini. .NET bisa belajar dari Java, tetapi tetap ada bagian yang mereka kacaukan
      Hal yang sama berlaku untuk MySQL. Di situs ini orang bilang “sudah mati”, tetapi bagi orang yang benar-benar paham, ia justru hidup kembali di bawah Oracle
    • Pernyataan “Oracle menelantarkan Java” dan “ditelantarkan sampai JDK 8” saling bertentangan
      Versi Java terakhir di bawah Sun keluar pada 2006, Oracle membeli Sun pada 2010, JDK 7 keluar pada 2011, dan JDK 8 pada 2014
      Timnya pada umumnya tetap sama, dan perbedaan terbesarnya adalah Oracle mengakhiri penelantaran itu dan menaruh lebih banyak dana. Karena itulah laju Java meningkat setelah akuisisi
      Disebut “mengejar ketertinggalan”, tetapi juga tidak jelas sebenarnya mengejar siapa. Bahasa yang sama atau lebih populer dari Java hanya JS/TS dan Python
      Orang yang bilang Java tertinggal biasanya membandingkannya dengan bahasa yang performanya jauh lebih buruk daripada Java. Orang yang menyukai fitur tertentu sering luput melihat bahwa bahasa yang memiliki fitur itu tetap lesu bukan karena tidak punya fitur tersebut, melainkan justru meskipun punya fitur itu
      Para manajer suka rilis yang cepat, sedangkan kepemimpinan teknis, bahkan sejak era Sun, justru bersikeras bahwa semuanya harus dilakukan dengan hati-hati, pelan, dan benar
      Saya paham suasana yang merasa Java tidak sepopuler tahun 2003, tetapi masa itu adalah periode yang sangat terpadu secara luar biasa bukan hanya untuk Java melainkan untuk seluruh ekosistem perangkat lunak, dan sebelum maupun sesudahnya tidak pernah seterpadu itu
    • Katanya “tulis sekali, distribusikan ke mana saja”, tapi tidak berlaku untuk browser, iOS, atau sistem embedded
      Sekarang teknologi yang benar-benar tulis sekali, distribusikan ke mana saja adalah WebAssembly. JVM sudah pernah mendapat gilirannya, dan kalah
    • Kalau analoginya dilanjutkan, Java bukan cuma dilempar ke garasi, tetapi juga dipakai untuk menggugat Google demi miliaran dolar tunjangan anak, dan akhirnya menjadi semacam alat penerima uang tunai
      Meski begitu, saya tetap tidak akan menyebut Java sampai “terhambat pertumbuhannya”. Ia membuat pilihan, sebagian masuk akal dan sebagian tidak, dan pilihan seperti itu sangat sulit diperbaiki di kemudian hari
      Lihat saja C++; menurut saya kompatibilitas-sebagian dengan C adalah albatros sepanjang 150 kaki yang mustahil diperbaiki, dan banyak versi setelah C++11 adalah upaya membuat albatros itu sedikit lebih bisa ditanggung
      Di JVM, memperlakukan semua value class sebagai satu L-type tunggal seperti tipe primitif menurut saya adalah solusi yang cukup rapi untuk masalah yang sulit
      Pada akhirnya semua ini berakar pada keputusan Java 2 untuk mengimplementasikan generics dengan type erasure demi kompatibilitas ke belakang, dan C3 melihat hasilnya lalu menolak menempuh jalan itu
  • Hanya dari evolusi value type di Java saja rasanya bisa ditulis satu novel thriller teknologi
    Saya membaca mailing list dan menonton semua video terkait, dan proses menyatukan desainnya menjadi sesuatu yang selalu terasa khas Java benar-benar mengesankan
    Pada saat yang sama, pembahasannya juga menggali jauh lebih rinci apa arti value type dan optimisasi apa yang bisa dilakukan, serta di bagian mana

    • Perubahan sintaksnya hanya menambahkan value
  • Pada value class, == pada dasarnya jadi bekerja seperti memcmp()
    Ini agak disayangkan, karena merusak enkapsulasi dan mengungkap detail implementasi
    Kode klien bisa bercabang tergantung bagaimana suatu nilai direpresentasikan secara internal. Dalam beberapa hal ini bahkan lebih buruk daripada perbandingan identitas, karena perbandingan identitas setidaknya tidak mengekspos keadaan internal

    • Value type adalah konsep yang sangat jauh dari cara berpikir berorientasi objek ala “organisme kotak hitam ajaib”
      Ini bukan melakukan OOP klasik dengan cara baru, melainkan bahasa yang lahir dari ideologi OOP melangkah lebih jauh ke dunia pasca-OOP
    • Jika sebongkah data punya keadaan internal, maka sebongkah data itu sendiri sudah keliru
      Saya rasa pihak Java pasti sudah cukup memikirkan hal-hal seperti mengecualikan padding dari perbandingan atau memaksa byte padding menjadi 0
      Ini juga harus berlaku untuk string. String jelas akan tetap dialokasikan di heap, dan melakukan memcmp pada pointer di dalam “struct” baru berarti tepatnya melakukan perbandingan identitas
    • Inti value class adalah bahwa ia tidak boleh mengenkapsulasi state, yakni sebagai penyimpan data yang sepenuhnya transparan
    • Jika Anda belum pernah memakai Java di dunia nyata, Anda mungkin tidak akan merasakan arti sebenarnya dari perubahan ini. Ini adalah perubahan yang merusak kompatibilitas yang jarang dilakukan Java
      Java memisahkan pemeriksaan identitas objek dan pemeriksaan kesetaraan. == pada dasarnya melihat apakah dua pointer sama, sedangkan kesetaraan adalah konsep subjektif yang berbasis antarmuka seperti equals/hashCode
      Jadi new Integer(1000) == new Integer(1000) dulu false tetapi sekarang menjadi true, dan new Integer(1000).equals(new Integer(1000)) adalah true, sementara new Integer(10) == new Long(10) dulu false tetapi sekarang menjadi galat kompilasi
      Di Java lama, integer di bawah nilai tertentu sering diganti dengan tipe yang dinormalkan, dan seingat saya angkanya sekitar 128. Karena itu ada perbedaan antara 10 dan 1000
      Sekarang tampaknya perbandingan di atas secara implisit melakukan unboxing. Fakta bahwa perbandingan Integer/Long dulu false dan sekarang menjadi galat kompilasi jelas menunjukkan bahwa unboxing ikut terlibat
      Mungkin dengan variabel Anda masih bisa mendapatkan perilaku lama
      Bagaimanapun, jika value class kehilangan identitas, maka == berubah dari kesetaraan pointer menjadi kesetaraan bit-level. Saya harap berbagai kasus pinggiran seperti ini diselesaikan, tetapi secara teknis ini memang perubahan yang merusak
  • Saya paham tujuan value class, tetapi implementasinya cacat
    Kode berikut akan mencetak apa? Point a = new Point(10, 10); Point b = a; a.x = 100; System.out.println(b.x);
    Sampai sekarang jawabannya jelas, tetapi jika value class ditambahkan, jawabannya bergantung pada apakah Point adalah value class atau reference class. Karena itu desain ini merusak keterbacaan
    Ini adalah pelanggaran terhadap prinsip keseragaman. Dalam The Psychology of Computer Programming karya Weinberg, keseragaman dijelaskan sebagai prinsip psikologis bahwa pengguna mengharapkan hal-hal yang tampak mirip akan bekerja dengan cara yang mirip, dan hal-hal yang tampak berbeda akan bekerja berbeda
    Jika bahasa pemrograman mengizinkan dua konstruksi yang di titik penggunaan tampak hampir sama memiliki perilaku semantik yang berbeda, beban kognitif pembaca meningkat. Untuk mengetahui apakah assignment, kesetaraan, identitas, dan mutasi bekerja seperti objek referensi biasa atau seperti nilai, pembaca harus memeriksa deklarasi tipe atau bergantung pada alat
    Ini bisa diperbaiki jika kata kunci value diwajibkan bukan hanya saat deklarasi tetapi juga saat penggunaan. Misalnya ditulis seperti value Point a = new Point(10, 10);

    • Jika melihat tujuan JEP 401, ini tidak mungkin. Sebab value class dimaksudkan agar pengembang bisa memilih model pemrograman untuk data immutable
      https://openjdk.org/jeps/401
    • Jika membaca artikelnya, baris ketiga adalah galat sintaks. Semua field pada value type bersifat final
      Tentu saja, hanya dari empat baris itu tidak ada cara untuk tahu hal seperti itu akan terjadi, tetapi masalah ini sebenarnya juga sudah ada sekarang. Hal yang sama akan terjadi jika Point adalah record
    • Pernyataan a.x = 100; tidak akan valid. Tipe record bersifat immutable
      Jadi situasi yang dikhawatirkan seharusnya tidak mungkin terjadi
    • Meski begitu tetap agak kabur
      Jika ditulis seperti value Point a = new Point(10, 10); value Point b copy= a; a.x uniq= 100; System.out.println(b.x);, akan jauh lebih jelas bahwa terjadi kloning/penyalinan dan bahwa perubahan field tidak memengaruhi field objek lain
  • Saya tahu di dunia Java mengakui keberadaan .NET itu seperti kurang sopan, tetapi saya penasaran bagaimana ini berbeda dari struct .NET
    Jika melihat sekilas value type, spesialisasi generik, dan boxing, tampaknya mereka membuat pilihan yang sama

    • Di C# memang ada cukup banyak jebakan, dan Java punya tujuan eksplisit untuk membuat hal ini lebih jelas
      Jika C# pada sudut pandang level rendah sebagian besar menyalin C, pihak Java mendekatinya dari level tinggi dan menganalisis secara rinci batasan mana yang memberi keuntungan apa
      Di bahasa lain, pembagian struct/class bersifat dikotomis, tetapi Java memungkinkan kontrol yang lebih rinci dengan mencerminkan makna domain dasarnya
      Dan pada struct khususnya, ternyata ada berbagai senjata makan tuan terutama dalam konteks paralel
    • Ada bagian di artikel yang membahas itu
      Secara pribadi saya melihat struct di C/C# bisa diubah dan diteruskan lewat penyalinan, sedangkan value class tidak bisa diubah dan diteruskan sebagai nilai
      Saya rasa alokasi stack tidak bisa dilakukan di Java
    • Secara fungsional tidak berbeda, Java hanya sedang mengejar praktik lama sekarang
      Dikotomi palsu seperti “struct C# punya identitas dan mutabilitas sehingga semantik penyalinan saat assignment atau passing harus didefinisikan secara tepat, dan karena itu memberi model yang lebih berat bagi programmer serta lebih sedikit kebebasan bagi runtime” tidak terlalu cocok dengan apa yang sedang dijelaskan
      Referensi kelas Java mungkin tidak punya identitas dalam arti semantik Java, tetapi dalam arti struktur memori unik pada alamat tertentu tentu saja tetap punya identitas. Ini nyaris sekadar bermain kata pada terminologi Java
  • Catatan kaki 6 tentang “apa bedanya dengan struct di C#” tidak akurat
    Melihat artikel itu dipenuhi gambar buatan AI, saya jadi bertanya-tanya apakah tulisan itu, atau setidaknya proses risetnya, juga penuh halusinasi

  • Tulisannya agak samar dan dramatis, tetapi syukurlah dokumen aslinya cukup mudah dibaca
    Halaman tingkat atas: https://openjdk.org/projects/jdk/28/spec/
    Status JEP: https://bugs.openjdk.org/secure/Dashboard.jspa?selectPageId=...
    Akan bagus kalau ada yang melacak perkembangan terkait di C#, Swift, Java, dan Rust. Menurut saya, semuanya telah berlomba untuk mengejar perangkat keras dan saling memengaruhi
    Secara pribadi, saya khawatir perubahan-perubahan ini akan berdampak pada berbagi memori FFI