4 puan yazan GN⁺ 4 시간 전 | 2 yorum | WhatsApp'ta paylaş
  • JEP 401: Value Classes and Objects gerçek bir JDK preview’suna girme aşamasına ulaştı
  • Temel amaç, Java nesnelerini “sınıf gibi kodlayıp int gibi çalışır” hale getirerek nesne başlığı·heap tahsisi·GC·pointer dolaylı başvurusu maliyetlerini azaltmak
  • JDK 28’deki value class, şimdilik hâlâ null alabilen referans türü; non-null türler, uzmanlaştırılmış generics ve 128 bit encoding buna dahil değil ve --enable-preview gerekiyor
  • JVM, value object’i scalarize edebilir veya alanlar ile dizilerde heap flattening uygulayabilir; ancak erased generic ya da Object gibi üst türlerde heap nesnesi olarak materialize edilebilir
  • Java geliştiricilerinin identity ile value arasındaki farkı kod tasarımına yansıtması gerekiyor; bunun etkisi ==, synchronized, primitive wrapper, dizi performansı ve gelecekteki generic uzmanlaştırmasına kadar uzanıyor

JDK 28’e gelen Valhalla kapsamı

  • 15 Haziran’da Oracle mühendisi Lois Foltan, JEP 401: Value Classes and Objects’un OpenJDK ana deposuna entegrasyonunu ve JDK 28 hedefini doğruladı
  • İlgili pull request, 1.816 dosya boyunca 197 binden fazla satır ekliyor
  • Değişikliklerin boyutu büyük olduğu için, entegrasyon sırasında diğer committer’lardan bir süre büyük commit’leri bekletmeleri istendi
  • JEP 401, varsayılan olarak kapalı bir preview özelliği
    • Sözdizimini kullanmak için --enable-preview gerekiyor
    • Brian Goetz bunu “Valhalla’nın ilk bölümü” olarak tanımladı
  • JDK 28’in 2027 Mart’ta yayımlanması planlanıyor; mainline entegrasyonunun ise 2026 Temmuz civarında yapılması öngörülüyor

Valhalla’nın hedef aldığı Java nesne modelinin maliyeti

  • Valhalla’nın sloganı “codes like a class, works like an int
    • Amaç, metotları, constructor doğrulaması ve anlamlı alan adları olan sıradan bir sınıf kullanırken JVM’in bunu primitive kadar verimli işleyebilmesi
  • Java’da 8 primitive dışında neredeyse her şey referans türü
    • Point p = new Point(1, 2) ifadesinde p, point’in kendisi değil, heap’teki nesneyi gösteren bir pointer
    • Her alan okumasında JVM bu pointer’ı takip etmek zorunda
  • Nesne sayısı arttıkça maliyet hızla büyüyor
    • Her nesnede tür, senkronizasyon durumu vb. için bir nesne başlığı bulunuyor
    • Nesneler heap’e tahsis ediliyor ve ardından GC’nin konusu oluyor
    • Bir milyon Point içeren dizi, gerçekte bir milyon pointer ile heap’in çeşitli yerlerine dağılmış bir milyon nesneden oluşuyor
  • Brian Goetz’in “State of Valhalla” metni bu bellek yerleşimini fluffy olarak adlandırıyor
    • Valhalla’nın hedefi ise verinin yan yana durduğu dense bir yerleşim

Donanım farkı ve escape analysis’in sınırları

  • Yüksek yoğunluklu bellek yerleşiminin önemli olmasının nedeni CPU ile bellek hızı arasındaki fark
    • 1995’te bellek erişiminin maliyeti CPU işlemlerine yakındı
    • Günümüzde CPU, ana bellekten iki basamak daha hızlı ve bu farkı cache kapatıyor
  • CPU genellikle belleği 64 baytlık cache line birimleriyle okuyor
    • Veri sıkı ve sıralı yerleşmişse tek seferde çok sayıda işe yarar değer getiriliyor
    • Pointer takip ederek dağınık nesnelere erişildiğinde cache miss oluşabiliyor ve bu, hit’ten çok daha yavaş olabiliyor
  • JVM’in escape analysis mekanizması bazı nesne tahsislerini ortadan kaldırabiliyor
    • Bir nesnenin yerel kod parçasının dışına “escape” etmediğine karar verilirse, heap’e tahsis etmek yerine alanlarını değişkenlere veya register’lara açabiliyor
  • Ancak escape analysis düşük öngörülebilirliğe sahip ve kırılgan
    • Nesne başka bir sınıfın alanına girerse, diziye kaydedilirse, karmaşık metotlara aktarılırsa veya JIT’in analiz edemediği sınırları aşarsa optimizasyon durabiliyor
    • Küçük bir refactoring, JDK güncellemesi veya kod yapısı değişikliği bile nesnenin yeniden heap’e çıkmasına yol açabiliyor
  • Performans için nesnelerden vazgeçip r, g, b gibi raw byte değerlerini doğrudan kodlamak hız kazandırabilir; ancak bunun karşılığında güvenlik·okunabilirlik·doğrulama·metotlar kaybediliyor

2014’te başlangıç ve Q World’den L World’e geçiş

  • Project Valhalla resmen 2014’te başladı
  • James Gosling bunu o dönemde “six PhDs tied into a single knot” diye tanımlamıştı
  • Java’nın yaratıcıları, Java 1.0 döneminden beri value type istiyordu; ancak 1995’te sorun fazla zordu ve vazgeçildi
  • İlk hedef, programlama modeli ile modern donanımın performans özellikleri arasındaki uyumu yeniden kurmaktı
    • Yönelim, kullanıcıların primitive gibi flat ve dense türleri doğrudan tanımlayabilmesi; ama bunların sıradan sınıflar gibi görünmesi ve çalışmasıydı
  • İlk prototype, Q World yaklaşımındaydı
    • Yeni value type’ı nesnelerden kökten farklı bir varlık sayıyor, ayrı bir type descriptor, bytecode ve top type kullanıyordu
    • Bu da JVM type system’in tamamının iki varyasyonu taşımasını gerektirdiği için karmaşıklığı artırıyordu
  • 2019 civarında ortaya çıkan L World dönüm noktası oldu
    • Value type, normal referanslarla aynı “L carrier”ı paylaşıyor
    • Ekip bunun zor bir entegrasyon olacağını düşünse de, büyük tavizler olmadan çalıştı ve önceki prototype’lardaki birçok sorunu çözdü
  • L World ile önemli bir ayrım ortaya çıktı
    • JVM modeli ile dil modelinin %100 örtüşmesi gerekmiyor
    • JVM’de L World modeli bulunurken, programcılara daha kullanışlı bir dil modeli sunulabiliyor
  • Sonraki çalışmalar, value class ve uzmanlaştırılmış generics olarak iki aşamaya ayrıldı

İsim ve modeldeki değişim

  • Valhalla terminolojisi birkaç kez değişti ve bu, basit bir ad değişikliğinden ziyade model değişimini yansıtıyordu
  • İlk terminoloji value types idi
    • O dönemde bu tiplerin tam olarak nasıl bir varlık olduğu henüz net değildi
  • 2019~2020 civarında inline classes modeli yerleşti
    • Mevcut sınıflar identity classes, yeni sınıflar ise identity'si olmayan inline classes olarak ayrılıyordu
    • inline class varsayılan olarak final'dı, alanları final'dı ve senkronize edilememesi gibi kısıtlar tanımlanmıştı
  • 2021 tarihli “State of Valhalla”, primitive classes ve iki projection modelini ele aldı
    • Tek bir tipin, düzleştirilmiş ve null kabul etmeyen bir value variant'a ve null kabul eden bir reference variant'a sahip olması tasarlanmıştı
    • Point.val / Point.ref, daha sonra da Point! / Point? gibi sözdizimleri denendi
  • Bu model güçlüydü ancak bilişsel yükü yüksekti
    • Programcıların aynı tipin iki biçimini ve aralarındaki dönüşüm zamanlarını günlük olarak anlaması gerekiyordu
    • Sonunda kullanıcı modelini sadeleştirmek için bu ikilik azaltıldı
  • Güncel JEP 401, value class ve value object terimlerini kullanıyor
    • value değiştiricisiyle value class tanımlanıyor
    • Instance'lar, identity'si olmayan value object'lerdir
    • value class hâlâ bir reference type'tır
  • non-nullability, ayrı bir opsiyonel JEP olan Null-Restricted Value Class Types içine ayrıldı
    • JDK 28'e dahil değil
  • Önceki “primitive classes” modelini açıklayan eski yazılar, güncel OpenJDK ölçütleriyle farklılık gösterebilir
  • JEP 401 ile birlikte preview durumundaki JEP 402: Enhanced Primitive Boxing da bulunuyor
    • primitive ile wrapper arasındaki dönüşümleri daha akıcı hâle getirmeyi hedefliyor
    • Tamamlanmış hâliyle JEP 401'le birlikte bütünüyle geleceği varsayılmamalı

JDK 28'in value class modeli

  • value class, value değiştiricisiyle tanımlanır
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 da mümkün
  • Başlıca kurallar şöyle
    • Tüm instance field'lar örtük olarak final'dır
    • method'lar synchronized olamaz
    • Sınıf varsayılan olarak final'dır
    • value class ve abstract value class'lardan oluşan hiyerarşiler mümkündür
    • identity'si olan bir sınıftan kalıtım alınamaz
    • interface implement etmek mümkündür
  • Temel özellik, identity'nin olmamasıdır
    • Normal nesneler aynı içeriğe sahip olsa bile new Point(1, 2) ile iki kez oluşturulduklarında birbirinden farklı nesnelerdir
    • value object'te ise int değeri 4 için “birbirinden farklı iki adet 4” olmaması gibi, identity yoktur

==, synchronized, null'daki değişim

  • value object'lerde ==, identity karşılaştırması değil, substitutability kontrolü olur
    • Aynı sınıfa ait olup olmadıkları ve aynı alan değerlerine sahip olup olmadıkları özyinelemeli olarak karşılaştırılır
    • primitive field'lar bit düzeyinde karşılaştırılır, object field'lar ise yine == ile karşılaştırılır
    • new USDCurrency(3,95) == new USDCurrency(3,95) ifadesi true olur
  • Ancak == iç durumu gördüğü için, “aynı veriyi ifade ediyor mu” sorusunda genellikle equals daha uygundur
  • value object'lerin senkronize edilecek bir identity'si yoktur
    • Senkronizasyon denenirse IdentityException oluşur
    • Identity'nin varlığını zorunlu olarak kontrol etmek gerektiğinde Objects.requireIdentity ve Objects.hasIdentity kullanılabilir
  • JDK 28'deki value class'lar hâlâ null olabilir
    • USDCurrency d = null; geçerlidir
    • null'u yasaklayan tipler, ayrı bir gelecekteki JEP konusudur ve JDK 28'de yoktur
  • non-nullability, basit bir sözdizimi meselesi değil, daha büyük value class'ların düzleştirilmesini mümkün kılan bir performans kaldıracıdır

Skalarlaştırma ve heap düzleştirme

  • JEP 401, JVM'e value object'leri optimize etme özgürlüğü verir
  • Skalarlaştırma (scalarization), bir value object reference'ını alanlar kümesine ayıran bir JIT tekniğidir
    • Color pointer'ı geçirmek yerine r, g, b byte'ları ve bir null durumu flag'i geçirilebilir
    • allocation ve GC maliyeti ortadan kalkabilir
    • escape analysis'e benzer, ancak daha öngörülebilirdir ve inline edilmemiş method call sınırlarının ötesinde uygulanabilir
  • Skalarlaştırmanın sınırlamaları vardır
    • Değişken tipi, value class'ın supertype'ı olan Object ise veya erased generic parameter ise genellikle çalışmaz
    • Bu durumda nesnenin heap üzerinde materialize edilmesi gerekir
  • Heap düzleştirme (heap flattening), value object'in alan değerlerini kompakt bir bit vektörü olarak kodlayıp bunları doğrudan alanlara veya dizi hücrelerine yazma yöntemidir
    • Başka bir heap konumunu gösteren pointer'lara gerek kalmaz
    • Veri yoğunluğu ve locality sağlar
  • Düzleştirilmiş verinin concurrent access altında tearing'i önlemek için atomic olarak okunup yazılabilmesi gerekir
    • Genel platformlarda “yeterince küçük” boyut, null flag'i de dahil olmak üzere 64 bit düzeyinde olabilir
    • Küçük value class'lar iyi düzleştirilebilir, ancak iki int field ya da tek bir double bile atomic write boyutuna uymuyorsa sıradan heap nesnesine dönüşebilir
  • Gelecekte 128 bit encoding ve null-restricted type, daha büyük value class'ların düzleştirilmesini mümkün kılabilir

Boxing, wrapper, dizilerdeki etkiler

  • preview açıldığında Integer, Long, Double gibi primitive wrapper class'ların kendisi value class olur
    • box identity'sini kaybettiği için JVM bunları skalarlaştırıp düzleştirebilir
    • Integer[], int[] verimliliğine yaklaşır ve boxing overhead önemli ölçüde azalır
  • JEP 402: Enhanced Primitive Boxing, primitive ile box arasındaki dönüşümleri daha da genişletir
    • List<int> gibi ifadelere giden yolu açar, ancak bu hâlâ ayrı ve olgunlaşmakta olan bir çalışmadır
  • Etki en net biçimde dizilerde görülür
    • Mevcut Color[], bir milyon pointer ve heap'e dağılmış bir milyon nesneye dönüşebilir
    • value class Color[], bitişik renk değerlerini doğrudan depolayan contiguous block olabilir
    • CPU, cache line bazında birden çok değeri sıralı biçimde okuyabilir

Point[] örneğiyle önce ve sonra farkı

  • Valhalla öncesindeki normal class örneği şöyledir
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];  
  • Bu dizi bir milyon pointer içerir
    • Her pointer, heap üzerinde bir yerde bulunan ayrı bir Point nesnesini işaret eder
    • Her nesnede iki int alanına ek olarak bir nesne başlığı da vardır
    • Dolaşım sırasında pointer okunmalı, ilgili adrese atlanmalı ve alanlar okunmalıdır
  • Valhalla sonrasında value class örneği şöyledir
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];  
  • Kod farkı value tek sözcüğüdür, ancak bellek yerleşimi değişir
    • JVM, her point’in değerini dizinin içinde sıkı biçimde depolayabilir
    • Her element için başlık yoktur, pointer da yoktur
    • x, y biçimindeki iki int temelinde 8 bayt ve olası bir null flag ile art arda yerleştirilebilir
  • Bakım kolaylığı da korunur
    • Point hâlâ adı, constructor’ı, doğrulaması ve method’ları olan bir class’tır
    • int[] xs, int[] ys olarak bölüp indeksleri eşleştirme yaklaşımından kaçınılabilir

Uzmanlaşmış generics neden hâlâ kaldı

  • Java generics, type erasure ile uygulanır
    • List<String> ve List<Integer>, runtime’da aynı Listtir
    • Type parameter T, Object olarak erasure’a uğrar
  • Erasure, Java’nın mevcut kod tabanını bozmadan generics’i eklemek için bilinçli bir seçimdi
    • Generic olmayan bir class’ı generic’e çevirseniz bile mevcut source file’lar ve derlenmiş class’lar bozulmaz
  • Valhalla ile erasure, performans açısından çatışır
    • List<Point> içine bir value object koyarsanız T, Object olarak erasure’a uğradığı için nesnenin heap üzerinde materialize edilmesi gerekir
    • Point[] ile elde edilen flattening avantajı, ArrayList<Point> içinde kaybolabilir
  • Toparlanma planı iki aşamalıdır
    • Universal Generics: Dil düzeyinde type variable’ın value type’ları da işleyebilmesini sağlar
      • Hâlâ erasure kullanır
      • T alanının varsayılan olarak null ile başlaması sorunu nedeniyle “null pollution” compiler warning’i ortaya çıkabilir
      • Bu uyarılar çözüldüğünde API, specialization-ready olmaya yaklaşır
    • Specialized Generics: JVM düzeyinde her concrete type argument için uzmanlaşmış class layout üretir
      • Proje terminolojisinde species ve type restriction bununla ilişkilidir
      • Ancak bu aşamada ArrayList<Point> gerçekten flat memory kullanabilir
  • JDK 28’de full specialized generics yoktur
    • Koleksiyonların, stream’lerin ve API’lerin value type üzerinde flat ve allocation-free hâle gelmesi, future release’lerdeki bir iştir

JDK 28’de olanlar ve olmayanlar

  • JDK 28’e gelenler şunlardır
    • value class ve value record bildirimleri
    • Primitive wrapper’lar gibi JDK’nin mevcut value-based class’larının value class’a geçişi
    • Koşulları sağlayan class’ların scalarization ve flattening’i
    • Daha ucuz boxing
  • JDK 28’de olmayanlar şunlardır
    • Null-restricted types
    • Full specialized generics
    • 128 bit encoding
    • Tam anlamıyla olgunlaşmış JEP 402
  • Bu bir preview feature olduğundan syntax ve davranış, her release’te geri bildirimlere göre değişebilir
  • JDK 28, LTS değildir
    • Bir sonraki LTS’nin Eylül 2027’deki JDK 29 olması muhtemeldir
    • Birçok şirket kararlı hâle gelmiş Valhalla ile LTS’te karşılaşabilir, ancak JDK 28 preview gerçek kod geri bildirim döngüsünü başlatır

Ekosistemde ve kodda oluşacak değişiklikler

  • Yüksek performanslı Java alanında Valhalla, abstraction’dan vazgeçmeden yoğun veriyi işleme yolu hâline gelir
    • Buna veri işleme, vektör hesaplama, ML, oyun geliştirme, finans ve codec gibi alanlar dahildir
  • Framework ve library’ler, value-based class geçişini başlatabilir
  • Identity’ye dayanan kodlar davranış farkları yaşayabilir
    • ==, value object’te address karşılaştırması değil substitutability karşılaştırması olur
    • synchronized, value object üzerinde IdentityException ile sonuçlanır
  • Integer, value class olsa bile çoğu durumda binary link vermeye devam eder
    • Yeni compilation error, bu tür tipler üzerinde senkronizasyon kurmaya çalışıldığında ortaya çıkar
    • Integer identity’sine dayanan == ya da synchronized(someInteger) etkilenebilir
  • Early-access build’ler jdk.java.net/valhalla adresinden kullanılabilir

Sık sorulan sorular özeti

  • value class, record ile aynı şey değildir
    • record, içeriğin component’lerden oluşmasını seçmektir
    • value, identity’den vazgeçmeyi seçmektir
    • Normal class, record, value class ve value record kombinasyonlarının hepsi mümkündür
  • Value object, == ile karşılaştırılabilir
    • Bunun anlamı address karşılaştırması değil substitutability’dir
    • Temsil ettiği verinin eşitliği için genellikle equals daha uygundur
  • JDK 28 value class’ları null olabilir
    • Non-nullable type, future bir JEP’tir
    • Bu, daha büyük value class’ların flattening’i için de önemlidir
  • Hızlı ve flat ArrayList<Point> henüz yok
    • Type erasure nedeniyle generic collection içindeki nesneler heap üzerinde materialize edilir
    • JDK 28’de flattening’in doğrudan çalıştığı tipik örnekler, value type alanları ve Point[] gibi dizilerdir
  • Escape analysis her şeyin yerini tutamaz
    • Nesne bir field’a, bir diziye ya da analizin sınırları dışına çıkarsa optimizasyon bozulabilir
    • Value object’in scalarization’ı daha öngörülebilirdir ve method-call boundary’lerini daha uzağa taşıyabilir
  • Valhalla’nın tamamı birkaç release boyunca genişleyecek
    • JDK 28, value class’ların ilk preview’sudur
    • Specialized generics, null-restricted types ve 128 bit encoding, future release’lere yayılan çalışmalardır

2 yorum

 
click 1 시간 전

Uzun bir süreye yayılarak yayımlanan Project Loom'un sanal thread'leri kullanışlı oldu ve JVM çalışma zamanı seviyesinde pek çok şeyi çözdüğü için geliştiricinin kafa yorması gereken şeyler nispeten az.
Project Valhalla da son sürüm olarak çıktığında buna benzer şekilde adeta bedava öğüne yakın bir his verirse güzel olur.

 
GN⁺ 4 시간 전
Hacker News yorumları
  • Bellek farkının temel bir nokta olduğu söylenmiş ama metnin gerçekten düzgün gözden geçirilip geçirilmediği şüpheli
    Az önceye kadar 64 bitten büyük gösterime sahip nesnelerin heap düzleştirmesi alamadığı anlatılmıyor muydu? Örnekteki Point, iki 32 bit tamsayıya ek olarak bir null bayrağı da içeriyor, yani en az 65 bit
    “Bir null bayrağı olabilir” ifadesi ve ardından gelen kısa vurgu cümleleri, sanki yapay zeka vurgu cümleleri üretirken konudan sapmış gibi hissettiriyor; ortadaki "[IMAGE: the same Point[] array in two variants..." bloğu da ayrıca üzücü

    • “Öğe başına header yok. Pointer yok. Heap içinde oradan oraya zıplamak yok.” gibi cümleler yapay zeka üslubu kokuyor ve bu yüzden tembelce bir yazım gibi görünüyor
      Yazarken yapay zekadan yardım almakta sorun yok ama kendi sesini katmıyorsan okumak için bir neden kalmıyor
      https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing#...
    • Konu gerçekten ilgi çekici göründüğü için okumak istemiştim; yapay zeka üretimi görsellere kadar da tahammül edebilirdim
      Ama birkaç paragraf sonra bunun bir LLM'den geçmiş ya da daha da kötü bir yöntemle üretilmiş bir yazı olduğu iyice belli oluyor
      Teknik blog olsun ya da başka bir şey, lütfen yazıları yapay zekaya yazdırmayın. Kimse böyle yazıları okumak istemiyor
    • Olası değer sayısı 18446744073709551616 iken bunlardan 1 tanesi null için ayrılamıyor mu yani? :)
      Bugün Rust'ta NonZeroU64 olduğunu ve Optional ile birleştirildiğinde öğe başına sadece 64 bit ile gereken davranışın elde edilebildiğini öğrenmiş oldum
      https://doc.rust-lang.org/std/num/type.NonZeroU64.html
    • O kadar fazla yapay zeka kullanıldığı o kadar belli ki, iki paragraf okuyup bıraktım
    • “64 bitten büyük gösterime sahip nesneler heap düzleştirmesi alamaz” ifadesi ilk commit için geçerliydi
      JEP'te de açıkça yazdığı gibi bu, devasa bir özelliğin yalnızca ilk çıktısı ve son dönem Java özelliklerinde olduğu gibi parça parça teslim ediliyor
      Elbette amaç daha büyük değerleri de düzleştirmek; mekanizma zaten JVM'in içinde mevcut. Geriye kalan şey, “tearing'e izin verilir” niyetini dil düzeyinde görünür kılmak
  • Valhalla'ya gerçekten konan emeği takdir ediyorum ama “model güçlüydü fakat zihinsel olarak ağırdı” yorumuna katılmak zor
    Bir değişkenin null olamayacağını söylemek zihinsel olarak ağır bir ayrım değil; özellikle de her şey yeterince açık şekilde işaretlenmişse hiç değil
    “Performans tavanından feragat etsek bile kullanıcı modelini basitleştirelim” yaklaşımı, aslında kullanıcıya daha fazla sadeleşme de sunmuş olabilirdi
    Programlama dillerindeki tip sistemleri, yalnızca sayılar işleyebilen CPU'lar üzerinde geliştiricilere kullanışlı güvenceler sunmak içindir. İsteğe bağlı güvenlik garantilerini “fazla karmaşık” diye azaltmaya gerek yok
    Hatta “dil modeli ile JVM modelinin %100 çakışması gerekmeyebilir” fikrine kadar gelinmişken bile

    • Java'nın yönünü doğru belirleyebileceğine çok güvenmiyorum
      Java yönetim yapısı yetersiz görünüyor ve özellikle baştan beri çoğunlukla doğru kararlar alan .NET tarafıyla karşılaştırınca bu daha da belirgin
      Bugünlerde Oracle içinde Java'nın gerçekten bir değeri ya da ilgi odağı olup olmadığını bile merak ediyorum. Şirket artık, veri merkezi/hesaplama işlerinin üzerine eklenmiş miras faaliyetler ve devasa borçlardan oluşan bir yapı gibi görünüyor
      Bazen Oracle'da hâlâ para kazanan tek birimlerin hukuk ekibiyle çim biçme bölümü olup olmadığını düşündüğüm oluyor
    • Bu şikayet Java diline değil de blog yazarına yönelik değil mi?
      Ayrıca null işaretleyicileri de geliyor: https://openjdk.org/jeps/8303099
      Sadece bunu kademeli olarak çıkarmaları gerekiyor; değer sınıfları/nesneleri getiren bu PR bile tek başına zaten 200 bin satır büyüklüğünde
    • Null kabul etmeyen değer tipleri sadece sonraki bir JEP'e bırakılmış
      Bunun imkansız olduğu söylenmiyor gibi; mesele, fili tek lokmada yiyememek
      Yine de bu filin bir bacağını epey uzun süredir kemiriyorlar
    • Nullable, railway-oriented programming içinde sadece başka bir yük durumu
      Eğer 2012'den beri çözülmüş bir kavramsa, durumun farklı türlerini doğrudan dile koymak için bir neden yok. Raylar ya A'ya gider ya B'ye; ayrım, trenin hangi yük durumunda olduğuna göre olur
      Bir kavram ortaya çıkıp kayboluyor ve dil savaşları çıkıyorsa, bu talep olduğu ama dilin bunu ya yeterince iyi karşılamadığı ya da karşılasa bile zihinsel yük oluşturduğu anlamına gelir
      Value, Errorstates, Null, IoExceptions, WeirdOsStatesNeededToHandleUpstairs gibi şeylerden söz ediyorum
      https://fsharpforfunandprofit.com/rop/
      Monty Python tarzında söylemek gerekirse, artık ilerleyelim
    • Burada konuşulan şey null güvenliği değil, Integer/int benzeri referans/değer izdüşümü meselesi
      Valhalla ekibi, her tip için kimlikli ve kimliksiz iki ayrı izdüşüm bulundurmak yerine, değer tiplerini baştan kimliksiz hale getirdi; böylece Integer ile int eşanlamlı oldu
      Bellek yerleşimi bağlama ve optimizasyon kararlarına göre otomatik belirleniyor. Bu yüzden Integer gibi temel sarmalayıcıların == anlamı da değişti ve artık “referans izdüşümü” mü yoksa “değer izdüşümü” mü kullanıldığına bağlı değil
      İsteğe bağlı güvenlik garantilerinin “zihinsel olarak ağır” diye azaltılması gibi bir durum burada yaşanmadı
  • Java/JVM ile ilgili HN yorumlarında tekrar tekrar görülen şey, şaşırtıcı derecede çok kişinin JVM ya da Java hakkında geçmişten kalma bir imgeye sahip olduğu ama bugünkü halini neredeyse hiç bilmediği
    2026'nın JVM'i oldukça sağlıklı bir yırtıcı. Kusurları var mı; elbette var, ama temeli son derece güçlü

    • HN'de JVM hakkında iyi yorum almak zor ve burada modası geçmiş bir teknoloji gibi görülüyor
      İşte en güncel Java 26'yı ve önizleme özelliklerini, özellikle de StructuredConcurrency'yi kullanıyorum ve harika. Daha önceki şirketlerde Haskell ve Python kullanmış biri olarak hiç pişman değilim
    • 2000'lerde Java'nın popüler olduğu dönemde başlamış Java monolitlerini bakımda tutarken hâlâ Java 8 çalıştırmak zorunda olan çok insan var
      Son birkaç yılda çıkan yeni özellikleri kişisel olarak biliyorum ama gerçek iş hayatında Java kelimenin tam anlamıyla geçmişe sıkışmış durumda
  • Buradaki yorumların önemli bir kısmı, şu anda yürütülen harika çalışmalara ve ileride gelecek daha da iyi JEP'lere kıyasla biraz haksız
    Java'yı bir çocuğa benzetirsek, ilk birkaç yıl sevgi dolu ebeveynler (Sun) tarafından büyütüldü, sonra diğer çocuklarla birlikte garaja atıldı ve kötü bir vasi (Oracle) tarafından ihmal edildi
    JDK 8'e kadar ihmal edilmiş ve sevilmemiş olduğu için temelde yetişmeye çalışıyordu
    “Ancak şimdi struct'lar ya da değer tipleri geliyor” denmesi doğru, ama bunun nedeni büyümesinin devasa, bürokratik ve düşmanca kurumsal süreçler yüzünden engellenmiş olması. Şimdi ise özgür ve OpenJDK ailesi aracılığıyla seviliyor
    Bundan sonra da bir kez yaz, her yere dağıt keyfini yaşamaya devam edecek

    • Oracle'ı sevseniz de sevmeseniz de, bu Java tarihinin doğru bir tasviri değil
      Daha çok, sevgi dolu ebeveynlerin büyüttüğü ama mali sorunlar nedeniyle koruyucu aileye bırakılan ve orada ihmal edilen bir çocuğa benziyor
      Sonra yeni ve sevgi dolu ebeveyn Oracle tarafından evlat edinildi ve Java gelişip sağlıklı, istikrarlı bir yetişkin oldu
      OpenJDK'yi referans uygulama hâline getirerek platformun açık kaynaklaşmasını tamamlayan da Oracle'dı; daha önce kapalı olan JFR ve Mission Control gibi araçları da açık kaynak yaptı
      Dil ekibinin ilk dönem üyelerinin çoğunu korudu; bu tür satın almalarda oldukça nadirdir ve Java hem dil hem de çalışma zamanı tarafında büyük ölçüde iyileşti
    • Java'nın ihmal edildiği dönem, Sun'ın son birkaç yılıydı
      Oracle, geriye dönük uyumluluğun büyük kısmını korurken Java'yı eşi görülmemiş bir hızla ileri taşıdı
      .NET için “en baştan doğru yaptılar” deniyor ama bu, .NET Framework/.NET Core/.NET ayrımı ve yeniden yazımları kastediyorsa, bu tartışma içinde bile pek anlamlı değil. .NET, Java'ya bakıp öğrenebilirdi ama yine de bazı şeyleri batırdı
      MySQL de aynı şekilde. Bu sitede “öldü” deniyordu ama gerçekten bilenler için Oracle altında yeniden canlandı
    • “Oracle Java'yı ihmal etti” ve “JDK 8'e kadar ihmal edildi” sözleri birbiriyle çelişiyor
      Sun altındaki son Java sürümü 2006'da çıktı, Oracle Sun'ı 2010'da satın aldı; JDK 7 2011'de, JDK 8 ise 2014'te çıktı
      Ekip büyük ölçüde aynı kaldı ve en büyük fark, Oracle'ın ihmali sona erdirip daha fazla kaynak sağlamasıydı. Bu yüzden satın alma sonrasında Java'nın hızı arttı
      “Yetişmeye çalışıyor” deniyor ama kime yetişmeye çalıştığı da belirsiz. Java kadar ya da daha popüler olan tek diller JS/TS ve Python
      Java'nın geri kaldığını söyleyenler genelde onu, aslında Java'dan çok daha kötü durumda olan dillerle kıyaslıyor. Belirli özellikleri sevenler, o özelliklere sahip dillerin o özellikler sayesinde değil, onlara rağmen zayıf performans gösterdiğini gözden kaçırabiliyor
      Yöneticiler hızlı sürüm çıkarmayı sever, buna karşılık Sun döneminden beri gelen teknik liderlik dikkatli, yavaş ve doğru yapılması gerektiğini savundu
      Java'nın 2003'teki kadar popüler olmaması hissini anlıyorum ama o dönem yalnızca Java için değil, tüm yazılım ekosistemi için istisnai derecede birleşik bir dönemdi ve öncesinde ya da sonrasında bir daha o kadar birleşik olmadı
    • “Bir kez yaz, her yere dağıt” deniyor ama tarayıcıda, iOS'ta ya da gömülü sistemlerde olmuyor
      Artık gerçekten bir kez yaz, her yere dağıt teknolojisi WebAssembly. JVM'nin sırası vardı ve kaybetti
    • Benzetmeyi sürdürürsek, Java sadece garaja atılmadı; ayrıca Google'a karşı milyarlarca dolarlık nafaka davası açmak için kullanıldı ve sonuçta bir nakit tahsil aracına dönüştü
      Yine de Java'nın “büyümesinin engellendiğini” söylemezdim. Seçimler yaptı; bazıları makuldü, bazıları değildi ve böyle seçimleri sonradan düzeltmek çok zor
      Sadece C++'a bakın; C ile yarı uyumluluğu, bence düzeltilemez 150 fitlik bir albatros ve C++11 sonrası birçok sürüm, o albatrosu biraz daha katlanılabilir hâle getirme çalışmasıydı
      JVM'de tüm değer sınıflarını temel tipler gibi tek bir L-tipi olarak ele almak, zor bir probleme oldukça zarif bir çözüm gibi görünüyor
      Sonuçta bunların hepsi, Java 2'nin geriye dönük uyumluluk uğruna generics'i type erasure ile uygulama kararına dayanıyor ve C3 sonuçları görüp o yolu reddetti
  • Sırf Java'nın değer tipi evrimi üzerine bile teknik gerilim romanı yazılabilir gibi
    E-posta listelerini okudum ve ilgili videoların hepsini izledim; tasarımı her zaman Java'ya özgü görünen bir şeye dönüştürme süreci gerçekten etkileyiciydi
    Aynı zamanda değer tipinin ne anlama geldiğini ve hangi optimizasyonların nerede yapılabileceğini çok daha ince ayrıntılarla ele aldı

    • Sözdizimindeki tek değişiklik value anahtar sözcüğünün eklenmesi
  • Değer sınıflarında == fiilen memcmp() gibi çalışmış oluyor
    Bu biraz hayal kırıklığı yaratıyor, çünkü kapsüllemeyi bozup uygulama ayrıntılarını açığa çıkarıyor
    İstemci kodu, verilen değerin içte nasıl temsil edildiğine göre dallanabilir. Bazı açılardan bu, kimlik karşılaştırmasından bile daha kötü; çünkü kimlik karşılaştırması en azından iç durumu ifşa etmez

    • Değer tipleri, “sihirli kara kutu organizma” tarzı nesne yönelimli düşünceye oldukça uzak bir kavram
      Bu, klasik nesne yönelimliliği yeni bir şekilde yapmak değil; nesne yönelimli ideallerden doğmuş bir dilin nesne-sonrası dünyaya bir adım daha atması
    • Bir veri yığınının iç durumu varsa, sorun zaten o veri yığınındadır
      Java tarafında da karşılaştırmada padding’i dışarıda bırakmayı veya padding baytlarını 0 olmaya zorlamayı kesinlikle düşünmüşlerdir diye tahmin ediyorum
      Bunun string’lerde de çalışması gerekir. String’ler açıkça heap üzerinde ayrılmaya devam edecek ve yeni “struct” içindeki işaretçileri memcmp ile karşılaştırmak tam olarak kimlik karşılaştırmasıdır
    • Değer sınıflarının özü, durumu kapsüllememeleri, yani tamamen şeffaf veri taşıyıcıları olmalarıdır
    • Java’yı pratikte kullanmadıysanız bu değişikliğin gerçek anlamını hissetmeyebilirsiniz. Bu, Java’nın nadiren yaptığı bir geriye dönük uyumluluğu bozan değişiklik
      Java, nesnenin kimliğini kontrol etmeyi ve eşitliği kontrol etmeyi ayırır. == temelde iki işaretçinin aynı olup olmadığına bakar; eşitlik ise equals/hashCode gibi arayüzlere dayanan öznel bir kavramdır
      Bu yüzden new Integer(1000) == new Integer(1000) eskiden false idi ama artık true oluyor; new Integer(1000).equals(new Integer(1000)) ise true; new Integer(10) == new Long(10) da eskiden false idi ama şimdi derleme hatası oluyor
      Eski Java’da belirli bir değerin altındaki tamsayılar kanonikleştirilmiş tiplere dönüştürülürdü; sanırım sınır 128 civarındaydı. Bu yüzden 10 ile 1000 arasında fark oluşuyordu
      Şimdi bu karşılaştırmalar örtük olarak unboxing yapıyor gibi görünüyor. Integer/Long karşılaştırmasının eskiden false olup şimdi derleme hatasına dönüşmesi, unboxing’in devreye girdiğini açıkça gösteriyor
      Değişkenlerle hâlâ eski davranışı elde etmek mümkün olabilir
      Her halükârda değer sınıfları kimliği kaybedince ==, işaretçi eşitliğinden bit düzeyinde eşitliğe dönüşüyor. Umarım bu çeşitli köşe durumlarını çözerler ama teknik olarak bu uyumluluğu bozan bir değişiklik
  • Değer sınıflarının amacını anlıyorum ama uygulama kusurlu
    Şu kod ne yazdırır? Point a = new Point(10, 10); Point b = a; a.x = 100; System.out.println(b.x);
    Şimdiye kadar cevap açıktı ama değer sınıfları eklenirse cevap, Point’in değer sınıfı mı yoksa referans sınıfı mı olduğuna göre değişir. Bu yüzden bu tasarım okunabilirliği zedeliyor
    Bu, tekbiçimlilik ilkesinin ihlali. Weinberg’in The Psychology of Computer Programming kitabında tekbiçimlilik, kullanıcının benzer görünen şeylerin benzer davranacağını, farklı görünen şeylerin de farklı davranacağını beklediğini söyleyen psikolojik ilke olarak açıklanır
    Bir programlama dili, kullanım noktasında neredeyse aynı görünen iki sözdizimine anlamsal olarak farklı davranışlar yüklerse, okurun bilişsel yükü artar. Atama, eşitlik, kimlik ve değişimin normal referans nesneleri gibi mi yoksa değerler gibi mi davrandığını anlamak için tip bildirimine bakmak veya araçlara güvenmek gerekir
    Bu durum, sadece bildirim anında değil kullanım anında da value anahtar sözcüğü isteseydi düzeltilebilirdi. Örneğin value Point a = new Point(10, 10); gibi

    • JEP 401’in hedeflerine bakınca bunun mümkün olmadığı görülüyor. Çünkü değer sınıfları, geliştiricinin değişmez veri için bir programlama modeli seçebilmesini amaçlıyor
      https://openjdk.org/jeps/401
    • Metni okuyunca üçüncü satırın sözdizimi hatası olduğu anlaşılıyor. Değer tipindeki tüm alanlar final olur
      Elbette sadece o dört satıra bakarak bunun böyle olacağını anlamanın yolu yok ama bu sorun şimdi de var. Point bir record olsa da aynı şey yaşanır
    • a.x = 100; ifadesi geçerli olmayacaktır. Çünkü record tipleri değişmezdir
      Dolayısıyla endişe edilen durumun gerçekleşmesi mümkün olmamalı
    • Yine de biraz muğlak
      value Point a = new Point(10, 10); value Point b copy= a; a.x uniq= 100; System.out.println(b.x); gibi yazılsaydı, klonlama/kopyalamanın gerçekleştiği ve alan değişikliğinin başka nesnelerin alanlarını etkilemediği çok daha açık olurdu
  • Java dünyasında .NET’in varlığını kabul etmenin kabalık sayıldığını biliyorum ama bunun .NET struct’larından farkı ne diye merak ediyorum
    Değer tipleri, jenerik özelleştirme ve boxing’e kabaca bakınca aynı seçimler yapılmış gibi görünüyor

    • C#’ta gerçekten epey tuzak var ve Java bunları daha açık hale getirmeyi hedefliyor
      C# düşük seviye bakış açısından büyük ölçüde C’yi kopyalayarak hareket ettiyse, Java tarafı daha yüksek seviyeden yaklaşıp hangi kısıtların hangi avantajları verdiğini ayrıntılı biçimde incelemiş
      Diğer dillerde struct/class ayrımı ikili bir yapıdayken Java, temel alanın anlamını yansıtmak için daha ince ayarlı denetime izin veriyor
      Ayrıca struct’larda, özellikle paralel bağlamlarda, çeşitli ayağa ateş etme durumlarının olduğu ortaya çıkıyor
    • Yazıda bu kısmı ele alan bir bölüm var
      Bana göre C/C# struct’ları değiştirilebilir ve kopyalanarak geçirilir; değer sınıfları ise değiştirilemez ve değer olarak geçirilir
      Java’da stack allocation yapılamayacağını düşünüyorum
    • İşlevsel olarak farklı değil; Java artık sadece eski bir geleneği yakalıyor
      “C# struct’ları kimlik ve değiştirilebilirliğe sahip olduğu için atama ya da geçirme sırasında kopyalama semantiğini kesin biçimde tanımlamak zorundadır; bu da programcıya daha ağır bir model, çalışma zamanına da daha az serbestlik verir” türü sahte ikilik, anlatılan şeyle pek örtüşmüyor
      Java sınıfı referans semantiğindeki kimlik olmayabilir ama belirli bir adresteki benzersiz bellek düzeni anlamında elbette hâlâ kimlik var. Bu daha çok Java terminolojisi üzerinden laf cambazlığına benziyor
  • Dipnot 6’daki “C#’ın struct’ı bundan nasıl farklı” kısmı isabetsiz
    Yazıda bir sürü AI ile üretilmiş görsel görünce, yazımın ya da en azından araştırma sürecinin de bolca halüsinasyon içerdiğini düşünmeden edemiyorum

  • Yazı biraz bulanık ve dramatik, ama neyse ki asıl belgeler epey okunabilir
    En üst düzey sayfa: https://openjdk.org/projects/jdk/28/spec/
    JEP durumu: https://bugs.openjdk.org/secure/Dashboard.jspa?selectPageId=...
    Keşke biri C#, Swift, Java ve Rust’taki ilgili gelişmeleri takip etse. Hepsinin donanıma yetişmek için yarıştığını ve birbirlerini etkilediğini düşünüyorum
    Kişisel olarak bu değişikliklerin FFI bellek paylaşımı üzerinde nasıl bir etkisi olacağı konusunda endişeliyim