- JEP 401: Value Classes and Objects अब वास्तविक JDK preview में शामिल होने के चरण तक पहुँच गया है
- मुख्य लक्ष्य Java objects को “class की तरह कोड करें, int की तरह काम करें” बनाना है, ताकि object header·heap allocation·GC·pointer indirection की लागत कम हो सके
- JDK 28 में value class अभी भी null-allowed reference type है; इसमें non-null type, specialized generics, और 128-bit encoding शामिल नहीं हैं, और
--enable-preview की आवश्यकता है
- JVM value object को scalarize कर सकता है या fields और arrays में heap flattening कर सकता है, लेकिन erased generic या
Object जैसे supertype में यह heap object के रूप में materialize हो सकता है
- Java developers को code design में identity और value के अंतर को शामिल करना होगा; इसका असर
==, synchronized, primitive wrapper, array performance, और आगे चलकर generic specialization तक जाएगा
JDK 28 में आने वाले Valhalla का दायरा
- 15 जून को Oracle engineer Lois Foltan ने JEP 401: Value Classes and Objects के OpenJDK main repository में integration और JDK 28 target की पुष्टि की
- संबंधित pull request ने 1,816 files में 197,000 से अधिक lines जोड़ीं
- बदलाव का दायरा इतना बड़ा था कि integration के दौरान दूसरे committers से कुछ समय के लिए बड़े commits रोकने का अनुरोध किया गया
- JEP 401 डिफ़ॉल्ट रूप से बंद रहने वाला preview feature है
- syntax इस्तेमाल करने के लिए
--enable-preview चाहिए
- Brian Goetz ने इसे “Valhalla का पहला हिस्सा” कहा
- JDK 28 की release मार्च 2027 में निर्धारित है, और mainline integration लगभग जुलाई 2026 के लिए योजनाबद्ध है
Java object model की लागत, जिसे Valhalla निशाना बना रहा है
- Valhalla का नारा है “codes like a class, works like an int”
- लक्ष्य यह है कि methods, constructor validation, और अर्थपूर्ण field names वाले सामान्य class का उपयोग करते हुए भी JVM उसे primitive की तरह कुशलता से संभाल सके
- Java में 8 primitives को छोड़कर लगभग सब कुछ reference type है
Point p = new Point(1, 2) में p, point स्वयं नहीं बल्कि heap object की ओर इशारा करने वाला pointer है
- हर बार field पढ़ते समय JVM को pointer के पीछे जाना पड़ता है
- objects की संख्या बढ़ने पर लागत तेज़ी से बढ़ती है
- हर object में type, synchronization state आदि के लिए object header होता है
- object heap पर allocate होता है और बाद में GC का target बनता है
Point के 10 लाख elements वाले array में वास्तव में 10 लाख pointers और heap में बिखरे 10 लाख objects होते हैं
- Brian Goetz के “State of Valhalla” में इस memory layout को fluffy कहा गया है
- Valhalla का लक्ष्य ऐसा dense layout है जिसमें data साथ-साथ रखा जाए
hardware gap और escape analysis की सीमाएँ
- dense memory layout महत्वपूर्ण होने का कारण CPU और memory speed के बीच का अंतर है
- 1995 में memory access की लागत CPU computation के लगभग बराबर थी
- आज CPU main memory की तुलना में कई गुना तेज़ है, और इस अंतर को cache भरता है
- CPU आम तौर पर memory को 64-byte cache line units में पढ़ता है
- अगर data घना और क्रम में हो, तो एक बार में अधिक उपयोगी values मिलती हैं
- pointer के पीछे बिखरे objects तक पहुँचने पर cache miss हो सकता है, और यह hit की तुलना में बहुत धीमा हो सकता है
- JVM की escape analysis कुछ object allocations को हटा सकती है
- अगर यह तय हो जाए कि object स्थानीय code block के बाहर “escape” नहीं करता, तो उसे heap पर allocate किए बिना उसके fields को variables या registers में फैलाया जा सकता है
- लेकिन escape analysis की predictability कम है और यह नाज़ुक है
- अगर object किसी दूसरी class के field में चला जाए, array में store हो, किसी complex method में pass हो, या ऐसी boundary पार करे जिसे JIT analyze न कर सके, तो optimization रुक सकता है
- छोटे refactoring, JDK update, या code structure में बदलाव से object फिर से heap पर जा सकता है
- performance के लिए object छोड़कर
r, g, b जैसे raw byte में सीधे encode करने से speed मिल सकती है, लेकिन safety·readability·validation·methods खो जाते हैं
2014 की शुरुआत और Q World से L World की ओर बदलाव
- Project Valhalla औपचारिक रूप से 2014 में शुरू हुआ
- James Gosling ने उस समय इसे “six PhDs tied into a single knot” कहा था
- Java के निर्माताओं को Java 1.0 के समय से ही value type चाहिए थे, लेकिन 1995 में समस्या इतनी कठिन थी कि उसे छोड़ दिया गया
- शुरुआती लक्ष्य programming model और modern hardware performance characteristics के बीच alignment को फिर से स्थापित करना था
- दिशा यह थी कि users खुद primitive की तरह flat और dense type घोषित कर सकें, लेकिन वे सामान्य class की तरह दिखें और काम करें
- शुरुआती prototype Q World दिशा में था
- इसमें नए value type को object से मूल रूप से अलग इकाई माना गया, और अलग type descriptor, bytecode, तथा top type रखे गए
- इससे पूरे JVM type system में दो variants रखने पड़ते थे, जिससे complexity बढ़ी
- लगभग 2019 में सामने आया L World एक turning point बना
- value type सामान्य reference के साथ वही “L carrier” साझा करता है
- टीम को लगा था कि यह integration कठिन होगा, लेकिन यह बड़े समझौते के बिना काम कर गया और पुराने prototype की कई समस्याएँ हल हो गईं
- L World में एक महत्वपूर्ण विभाजन सामने आया
- JVM model और language model का 100% एक जैसा होना ज़रूरी नहीं है
- JVM में L World model रखा जा सकता है, जबकि programmers को अधिक सुविधाजनक language model दिया जा सकता है
- इसके बाद का काम value class और specialized generics के दो चरणों में बँट गया
नाम और मॉडल में बदलाव
- Valhalla की शब्दावली कई बार बदली, और यह सिर्फ नाम बदलना नहीं था बल्कि मॉडल में बदलाव को भी दिखाता था
- शुरुआती शब्द value types था
- उस समय यह अभी स्पष्ट नहीं था कि ये टाइप्स वास्तव में क्या होंगे
- 2019~2020 के आसपास inline classes मॉडल स्थापित हुआ
- मौजूदा classes को identity classes और नई classes को identity रहित inline classes के रूप में अलग किया गया
- inline class डिफ़ॉल्ट रूप से final होती थी, fields final होते थे, और synchronize न किए जा सकने जैसी पाबंदियाँ तय की गईं
- 2021 के “State of Valhalla” में primitive classes और two-projection मॉडल पर चर्चा की गई
- विचार यह था कि एक ही type के पास flat और non-null value variant तथा null स्वीकार करने वाला reference variant होगा
Point.val / Point.ref, और बाद में Point! / Point? जैसे syntax भी आज़माए गए
- यह मॉडल शक्तिशाली था, लेकिन समझने का बोझ ज़्यादा था
- प्रोग्रामर को एक ही type के दो रूपों और उनके बीच conversion के समय को रोज़मर्रा में समझना पड़ता था
- अंततः user model को सरल बनाने के लिए dualism को कम किया गया
- अभी JEP 401 में value class और value object का उपयोग किया जाता है
value modifier से value class घोषित की जाती है
- instance, identity रहित value object होता है
- value class अभी भी reference type ही है
- non-nullability को अलग optional JEP, Null-Restricted Value Class Types, में विभाजित किया गया है
- यह JDK 28 में शामिल नहीं है
- पुराने लेख जो पहले के “primitive classes” मॉडल को समझाते हैं, वे मौजूदा OpenJDK मानक से अलग हो सकते हैं
- JEP 401 के साथ preview रूप में JEP 402: Enhanced Primitive Boxing भी है
- इसका लक्ष्य primitive और wrapper के बीच conversion को अधिक सहज बनाना है
- यह मान लेना सही नहीं होगा कि पूरा हुआ रूप JEP 401 के साथ ही सब कुछ बनकर आ जाएगा
JDK 28 का value class मॉडल
- value class को
value modifier से घोषित किया जाता है
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 भी संभव है
- मुख्य नियम इस प्रकार हैं
- सभी instance fields implicitly final होते हैं
- method
synchronized नहीं हो सकती
- class डिफ़ॉल्ट रूप से final होती है
- value class और abstract value class से बनी hierarchy संभव है
- identity वाली class को inherit नहीं किया जा सकता
- interface implement करना संभव है
- इसकी मुख्य विशेषता है identity का न होना
- सामान्य object में, समान सामग्री होने पर भी
new Point(1, 2) से दो बार बनाने पर वे अलग object होते हैं
- value object में
int मान 4 की तरह identity नहीं होती; “अलग-अलग दो 4” जैसी कोई बात नहीं होती
==, synchronized, null में बदलाव
- value object में
== identity comparison नहीं बल्कि substitutability की जाँच बन जाता है
- यह recursively जाँचता है कि class एक ही है और field values समान हैं या नहीं
- primitive field की तुलना bit स्तर पर होती है, और object field की तुलना फिर
== से की जाती है
new USDCurrency(3,95) == new USDCurrency(3,95) true होगा
- फिर भी
== आंतरिक state को देखता है, इसलिए “क्या यह वही data दर्शाता है” के लिए आमतौर पर equals अधिक उपयुक्त है
- value object में synchronize करने लायक identity नहीं होती
- synchronize करने की कोशिश पर IdentityException होगा
- जब identity की अनिवार्य जाँच करनी हो, तब
Objects.requireIdentity और Objects.hasIdentity का उपयोग किया जा सकता है
- JDK 28 की value class अभी भी null स्वीकार कर सकती है
USDCurrency d = null; वैध है
- null को निषिद्ध करने वाला type अलग future JEP का विषय है और JDK 28 में नहीं है
- non-nullability सिर्फ syntax का मुद्दा नहीं है, बल्कि बड़ी value classes के flattening को संभव बनाने वाला performance lever भी है
Scalarization और heap flattening
- JEP 401 JVM को value object optimize करने की स्वतंत्रता देता है
- scalarization एक JIT तकनीक है जिसमें value object reference को fields के समूह में तोड़ा जाता है
Color pointer पास करने के बजाय r, g, b byte और null-स्थिति flag पास किए जा सकते हैं
- इससे allocation और GC लागत गायब हो सकती है
- यह escape analysis जैसा है, लेकिन अधिक predictable है और non-inlined method call boundaries के पार भी लागू हो सकता है
- scalarization की सीमाएँ भी हैं
- अगर variable type, value class का supertype
Object हो या erased generic parameter हो, तो यह आमतौर पर काम नहीं करता
- ऐसे में object को heap पर materialize करना पड़ता है
- heap flattening वह तरीका है जिसमें value object की field values को compact bit vector के रूप में encode करके field या array cell में सीधे लिखा जाता है
- किसी दूसरी heap location की ओर इशारा करने वाले pointer की ज़रूरत नहीं पड़ती
- इससे data density और locality मिलती है
- flatten किए गए data को concurrent access में tearing से बचाने के लिए atomic रूप से पढ़ा-लिखा जा सकना चाहिए
- सामान्य platforms पर “काफ़ी छोटा” आकार null flag सहित 64-bit स्तर का हो सकता है
- छोटी value classes अच्छी तरह flatten हो सकती हैं, लेकिन दो
int fields या सिर्फ एक double होने पर भी atomic write size में फिट न होने की वजह से वह सामान्य heap object बन सकती है
- आगे चलकर 128-bit encoding और null-restricted type बड़ी value classes के flattening को संभव बना सकते हैं
Boxing, wrapper, और arrays में प्रभाव
- preview चालू होने पर
Integer, Long, Double जैसी primitive wrapper classes खुद value class बन जाती हैं
- box identity खो देता है, इसलिए JVM उसे scalarize और flatten कर सकता है
Integer[], int[] की efficiency के काफ़ी करीब जा सकता है, और boxing overhead को बहुत कम करने की दिशा है
- JEP 402: Enhanced Primitive Boxing primitive और box के बीच conversion को और विस्तृत करता है
- यह
List<int> जैसे expression की दिशा में रास्ता खोलता है, लेकिन अभी भी अलग से परिपक्व हो रहा काम है
- arrays में इसका असर सबसे स्पष्ट दिखता है
- मौजूदा
Color[], दस लाख pointers और heap में बिखरे दस लाख objects बन सकता है
- value class
Color[], सीधे लगातार color values को स्टोर करने वाला contiguous block बन सकता है
- CPU cache line इकाइयों में कई values को क्रम से पढ़ सकता है
Point[] उदाहरण में पहले और बाद का अंतर
- Valhalla से पहले की सामान्य class का उदाहरण इस प्रकार है
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 दस लाख pointer रखता है
- हर pointer heap में कहीं मौजूद अलग
Point object की ओर इशारा करता है
- हर object में दो
int के अलावा object header भी होता है
- traversal करते समय pointer पढ़ना, उस address पर jump करना, और फिर field पढ़ना पड़ता है
- Valhalla के बाद value class का उदाहरण इस तरह है
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];
- code में फर्क सिर्फ
value एक शब्द का है, लेकिन memory layout बदल जाता है
- JVM हर point का मान array के अंदर densely store कर सकता है
- हर element के लिए न header होता है, न pointer
- दो
int यानी x, y के हिसाब से 8 byte और संभावित null flag के साथ इन्हें लगातार रखा जा सकता है
- maintainability भी बनी रहती है
Point अब भी नाम, constructor, validation और method वाला class है
int[] xs, int[] ys में तोड़कर index मिलाने वाला तरीका टाला जा सकता है
specialized generics अभी भी बाकी क्यों है
- Java generics को type erasure से implement किया गया है
List<String> और List<Integer> runtime पर एक ही List होते हैं
- type parameter
T का erasure होकर Object बन जाता है
- erasure, Java के मौजूदा codebase को तोड़े बिना generics लाने के लिए एक जानबूझकर किया गया चुनाव था
- non-generic class को generic में बदलने पर भी पुराने source file और compiled class नहीं टूटते
- Valhalla और erasure में performance के स्तर पर टकराव है
List<Point> में value object डालने पर T का erasure होकर Object बनता है, इसलिए object को heap पर materialize होना पड़ता है
Point[] से मिलने वाला flattening का फायदा ArrayList<Point> में गायब हो सकता है
- recovery plan दो चरणों का है
- Universal Generics: language level पर type variable को value type तक handle करने देना
- इसमें अब भी erasure इस्तेमाल होगा
T field का default रूप से null से शुरू होना “null pollution” compiler warning ला सकता है
- warning हल करने पर API specialization-ready के करीब आ जाती है
- Specialized Generics: JVM level पर हर concrete type argument के लिए specialized class layout बनाना
- project terminology में species और type restriction इससे जुड़े हैं
- इसी चरण में
ArrayList<Point> सचमुच flat memory का इस्तेमाल कर सकेगा
- JDK 28 में full specialized generics नहीं है
- collection, stream और API का value type पर flat और allocation-free होना future release का काम है
JDK 28 में क्या है और क्या नहीं है
- JDK 28 में आने वाली चीजें ये हैं
value class और value record declaration
- JDK की मौजूदा value-based class में से primitive wrapper जैसी class का value class migration
- शर्तें पूरी करने वाले class की scalarization और flattening
- सस्ता boxing
- JDK 28 में ये चीजें नहीं हैं
- null-restricted types
- full specialized generics
- 128-bit encoding
- पूरी तरह mature JEP 402
- यह preview feature है, इसलिए syntax और behavior feedback के आधार पर हर release में बदल सकते हैं
- JDK 28, LTS नहीं है
- अगला LTS संभवतः सितंबर 2027 का JDK 29 होगा
- कई कंपनियां stabilized Valhalla को LTS में देखेंगी, लेकिन JDK 28 preview असली code feedback loop की शुरुआत करता है
ecosystem और code में आने वाले बदलाव
- high-performance Java के क्षेत्र में Valhalla, abstraction छोड़े बिना dense data handle करने का रास्ता बनता है
- इसमें data processing, vector computation, ML, game development, finance और codec जैसे क्षेत्र शामिल हैं
- framework और library value-based class migration शुरू कर सकते हैं
- identity पर निर्भर code में behavior का फर्क आ सकता है
- value object में
==, address comparison नहीं बल्कि substitutability comparison बन जाता है
- value object पर
synchronized करने से IdentityException होगा
Integer के value class बनने पर भी ज्यादातर मामलों में binary link होती रहेगी
- नया compilation error तब आएगा जब ऐसे type पर synchronization करने की कोशिश होगी
Integer identity पर निर्भर == या synchronized(someInteger) प्रभावित हो सकते हैं
- early-access build jdk.java.net/valhalla पर उपलब्ध है
अक्सर पूछे जाने वाले सवाल
- value class, record से अलग है
record का मतलब है content को component के रूप में चुनना
value का मतलब है identity छोड़ना
- सामान्य class, record, value class और value record—सभी संयोजन संभव हैं
- value object की तुलना
== से की जा सकती है
- इसका मतलब address comparison नहीं बल्कि substitutability है
- जो data यह दर्शाता है, उसकी समानता के लिए आम तौर पर
equals ज्यादा उपयुक्त है
- JDK 28 value class में null संभव है
- non-nullable type future JEP का हिस्सा है
- बड़े value class की flattening में भी यह महत्वपूर्ण है
- तेज flat
ArrayList<Point> अभी नहीं है
- type erasure की वजह से generic collection के अंदर object को heap पर materialize होना पड़ता है
- JDK 28 में flattening सीधे काम करने का प्रमुख उदाहरण value type के field और
Point[] जैसे array हैं
- escape analysis सब कुछ replace नहीं कर सकता
- object अगर field, array या analysis boundary के बाहर चला जाए तो optimization टूट सकता है
- value object की scalarization ज्यादा predictable होती है और method-call boundary के पार भी ज्यादा दूर तक जा सकती है
- पूरा Valhalla कई release में फैलेगा
- JDK 28, value class का पहला preview है
- specialized generics, null-restricted types और 128-bit encoding future release में आने वाला काम है
2 टिप्पणियां
Project Loom में लंबे समय में रिलीज़ हुए virtual threads काफ़ी सुविधाजनक हैं और JVM runtime level पर बहुत-सी चीज़ें संभाल लेते हैं, इसलिए डेवलपर को कम सोचना पड़ता है.
उम्मीद है कि Project Valhalla भी जब final release हो, तो कुछ हद तक ऐसे ही "free lunch" जैसा महसूस हो.
Hacker News टिप्पणियाँ
कहा गया कि मेमरी का फ़र्क बुनियादी हिस्सा है, लेकिन शक है कि लेख की ठीक से समीक्षा हुई भी थी या नहीं
अभी थोड़ी देर पहले तक यही तो समझाया नहीं गया था कि 64-bit से बड़े representation वाले object heap flattening नहीं पाते? उदाहरण वाला
Pointतो 32-bit integer 2 और null flag तक रखता है, यानी कम से कम 65-bit है“null flag हो भी सकता है” जैसी अभिव्यक्ति और उसके बाद आने वाले छोटे-छोटे ज़ोरदार वाक्य देखकर लगता है जैसे AI ने emphasis line बनाते-बनाते रास्ता बदल लिया हो, और बीच का
"[IMAGE: the same Point[] array in two variants..."ब्लॉक भी अफ़सोसजनक हैAI से लिखने में मदद लेना ठीक है, लेकिन अगर अपनी आवाज़ ही न हो तो पढ़ने की वजह नहीं बचती
https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing#...
लेकिन कुछ paragraph बाद ही साफ़ हो गया कि यह LLM से गुज़रा हुआ, या उससे भी बदतर तरीके से बना लेख है
टेक ब्लॉग हो या कुछ भी, कृपया AI से लिखवाना बंद करें। कोई भी ऐसा लेख पढ़ना नहीं चाहता
आज पता चला कि Rust में
NonZeroU64है, औरOptionalके साथ मिलाकर प्रति item सिर्फ 64-bit में ज़रूरी behavior मिल सकता हैhttps://doc.rust-lang.org/std/num/type.NonZeroU64.html
JEP में भी साफ़ है कि यह इस बहुत बड़े feature का सिर्फ पहला deliverable है, और हाल की Java features की तरह इसे भी टुकड़ों में दिया जा रहा है
साफ़ है कि लक्ष्य बड़े value को भी flatten करना है, और उसका mechanism JVM के अंदर पहले से मौजूद है। अब बस भाषा स्तर पर “tearing की अनुमति है” इस इरादे को व्यक्त करना बाकी है
Valhalla में वास्तव में डाले गए काम की मेहनत मानता हूँ, लेकिन “model शक्तिशाली था पर मानसिक रूप से भारी था” वाली व्याख्या से सहमत होना मुश्किल है
यह कहना कि कोई variable null नहीं हो सकता, मानसिक रूप से बोझिल भेद नहीं है, खासकर जब सब कुछ पर्याप्त रूप से annotate किया गया हो
“performance ceiling की क़ीमत पर user model को सरल बनाना” वाला रवैया उल्टा शायद users के लिए ज़्यादा सरलता देता
प्रोग्रामिंग भाषा का type system इसलिए होता है कि सिर्फ numbers संभाल सकने वाले CPU के ऊपर developers को सुविधाजनक guarantees मिल सकें। वैकल्पिक safety guarantees को “बहुत जटिल” कहकर घटाने की ज़रूरत नहीं है
यहाँ तक कि वे इस समझ तक पहुँच चुके हैं कि “language model और JVM model को 100% एक जैसा होने की ज़रूरत नहीं”
Java governance कमज़ोर लगती है, और यह .NET से खास तौर पर विपरीत दिखती है, जिसने शुरुआत से ज़्यादातर सही फ़ैसले लिए
आजकल तो यह भी शक होता है कि Oracle के भीतर Java की कोई क़ीमत या प्राथमिकता बची भी है या नहीं। कंपनी अब ऐसी लगती है जैसे data center/compute business पर legacy गतिविधियाँ और भारी कर्ज़ लटका हो
कभी-कभी लगता है कि Oracle के भीतर अब भी कमाई कराने वाले हिस्से सिर्फ legal team और lawnmower division ही बचे हैं
और null marker भी आने वाला है: https://openjdk.org/jeps/8303099
बस इसे धीरे-धीरे जारी करना पड़ेगा, और value class/object लाने वाला यह PR अकेला ही 200,000 lines का है
ऐसा नहीं लगता कि इसे असंभव कहा जा रहा है, बस यह कि हाथी को एक ही कौर में नहीं खाया जा सकता
फिर भी, इसकी यह एक टांग काफ़ी लंबे समय से कुतरी जा रही है
अगर 2012 से यह सुलझा हुआ विचार है, तो state के इतने रूप सीधे भाषा में डालने की ज़रूरत नहीं है। रेल या तो A जाती है या B, और बंटवारा ट्रेन के cargo state के हिसाब से होता है
अगर कोई विचार आता-जाता रहे और language war छिड़ी रहे, तो यह संकेत है कि मांग तो है लेकिन भाषा उसे ठीक से संभाल नहीं पा रही, या संभालकर भी मानसिक overload पैदा कर रही है
जैसे
Value,Errorstates,Null,IoExceptions,WeirdOsStatesNeededToHandleUpstairshttps://fsharpforfunandprofit.com/rop/
Monty Python के अंदाज़ में कहें तो, अब आगे बढ़ें
Integer/intजैसे reference/value projection की हो रही हैValhalla team ने हर type के लिए identity वाला projection और बिना identity वाला projection, दोनों रखने के बजाय value type को ही identity-रहित बना दिया, इसलिए
Integerऔरintएक ही अर्थ वाले हो जाते हैंmemory layout context और optimization decisions के हिसाब से अपने-आप तय होगा। इसी वजह से
Integerजैसे primitive wrapper का==semantics भी बदल गया, और अब यह इस पर निर्भर नहीं करता कि आप “reference projection” इस्तेमाल कर रहे हैं या “value projection”यहाँ वैकल्पिक safety guarantees को “मानसिक रूप से बोझिल” कहकर कम नहीं किया गया है
Java/JVM से जुड़े HN कमेंट्स में बार-बार जो बात दिखती है, वह यह है कि चौंकाने वाली संख्या में लोगों के पास JVM या Java की पुरानी छवि तो है, लेकिन आज का रूप उन्हें लगभग पता ही नहीं है
2026 का JVM एक बेहद स्वस्थ शिकारी है। खामियां हैं क्या? बिल्कुल हैं, लेकिन इसकी नींव असाधारण रूप से मजबूत है
काम में मैं नवीनतम Java 26 और preview features, खासकर
StructuredConcurrency, इस्तेमाल करता हूं और यह शानदार है। पहले की कंपनियों में Haskell और Python इस्तेमाल करने के बाद भी मुझे इसमें ज़रा भी पछतावा नहीं हैपिछले कुछ सालों में आए नए features के बारे में मैं व्यक्तिगत रूप से जानता हूं, लेकिन असली कामकाज में Java सचमुच अतीत में फंसा हुआ है
यहां के कई कमेंट्स अभी चल रहे शानदार काम और आगे आने वाले उससे भी बेहतर JEPs की तुलना में कुछ हद तक unfair हैं
अगर Java की तुलना एक बच्चे से करें, तो उसके शुरुआती कुछ साल प्यार करने वाले माता-पिता (Sun) के साथ बीते, फिर उसे दूसरे बच्चों के साथ गैरेज में फेंक दिया गया और एक दुष्ट अभिभावक (Oracle) के पास उपेक्षित छोड़ दिया गया
JDK 8 तक उसे उपेक्षा और प्यार की कमी मिली, इसलिए मूल रूप से उसे catch-up खेलना पड़ा
“अब जाकर structs या value types जैसी चीज़ें आई हैं” यह बात सही है, लेकिन ऐसा इसलिए हुआ क्योंकि एक विशाल, नौकरशाही और शत्रुतापूर्ण corporate process ने उसकी बढ़त रोक दी। अब वह आज़ाद है और OpenJDK परिवार के ज़रिए उसे प्यार मिल रहा है
आगे भी write once, deploy anywhere का आनंद मिलता रहेगा
ज़्यादा सही यह होगा कि उसे प्यार करने वाले माता-पिता ने आर्थिक समस्याओं के कारण एक foster family के पास छोड़ दिया, जहां उसकी उपेक्षा हुई
इसके बाद नए और स्नेही माता-पिता Oracle ने उसे गोद लिया, और Java खिल उठा, एक स्वस्थ और स्थिर वयस्क बन गया
OpenJDK को reference implementation बनाकर platform का open source करना पूरा करने वाला भी Oracle ही था, और पहले proprietary रहे JFR, Mission Control जैसे tools को भी उसने open source किया
language team के बहुत से शुरुआती सदस्यों को भी उसने बनाए रखा, जो इस तरह के acquisition में काफी दुर्लभ बात है, और Java में language और runtime दोनों स्तरों पर बड़े सुधार हुए
Oracle ने अधिकांश backward compatibility बनाए रखते हुए भी Java को अभूतपूर्व रफ्तार से आगे बढ़ाया
.NET को “शुरू से सही किया गया” कहा जाता है, लेकिन अगर उसका मतलब .NET Framework/.NET Core/.NET के विभाजन और rewrites से है, तो इस चर्चा के भीतर भी यह बात नहीं टिकती। .NET, Java को देखकर सीख सकता था, फिर भी उसने कुछ चीज़ें बिगाड़ीं
MySQL के साथ भी यही हुआ। इस साइट पर उसे “मरा हुआ” कहा गया, लेकिन जो लोग सच में जानते हैं, उनके लिए Oracle के तहत वह फिर जीवित हुआ
Sun के तहत आखिरी Java version 2006 में आया था, Oracle ने 2010 में Sun को खरीदा, JDK 7 2011 में और JDK 8 2014 में आया
टीम ज़्यादातर वही रही, और सबसे बड़ा फर्क यह था कि Oracle ने उपेक्षा खत्म की और ज़्यादा funding दी। इसी वजह से acquisition के बाद Java की रफ्तार तेज़ हुई
इसे “catch-up” कहना भी अस्पष्ट है, क्योंकि यह साफ नहीं कि किससे catch-up करना था। Java जितनी या उससे ज़्यादा लोकप्रिय भाषाएं सिर्फ JS/TS और Python हैं
जो लोग कहते हैं कि Java पीछे रह गया, वे आमतौर पर उसकी तुलना उन भाषाओं से करते हैं जो Java से कहीं कम सफल हैं। लोग किसी खास feature को पसंद करते हैं और यह नज़रअंदाज़ कर देते हैं कि वह feature होने के बावजूद वह भाषा पिछड़ रही है, उसकी वजह से आगे नहीं बढ़ रही
managers तेज़ी से shipping पसंद करते हैं, जबकि Sun के समय से मौजूद technical leadership अक्सर सावधानी से, धीरे और सही तरीके से करने पर ज़ोर देती रही है
यह भावना समझ आती है कि Java अब 2003 जितना लोकप्रिय नहीं है, लेकिन वह दौर सिर्फ Java के लिए नहीं, पूरे software ecosystem के लिए असाधारण रूप से एकीकृत समय था, और उससे पहले या बाद में वैसी एकरूपता कभी नहीं रही
अब असली write once, deploy anywhere तकनीक WebAssembly है। JVM की बारी आई थी, और वह हार गया
फिर भी मैं इसे Java की “growth stunted” स्थिति नहीं कहूंगा। कुछ choices की गईं; कुछ तर्कसंगत थीं, कुछ नहीं, और ऐसी choices को बाद में ठीक करना बेहद कठिन होता है
सिर्फ C++ को देख लीजिए; C के साथ उसकी semi-compatibility को मैं व्यक्तिगत रूप से 150-foot albatross मानता हूं जिसे हटाया नहीं जा सकता, और C++11 के बाद के कई versions उस albatross को थोड़ा और सहनीय बनाने की कोशिश भर रहे हैं
JVM में सभी value classes को primitive types की तरह एकल L-type के रूप में संभालना, मेरी नज़र में, एक कठिन समस्या का काफी साफ-सुथरा समाधान है
आखिरकार यह सब Java 2 के उस निर्णय से निकला जिसमें backward compatibility के लिए generics को type erasure के साथ लागू किया गया, और C3 ने उस नतीजे को देखकर वही रास्ता ठुकरा दिया
Java में value types के evolution पर ही शायद एक पूरी तकनीकी thriller किताब लिखी जा सकती है
मैंने mailing lists पढ़ीं और संबंधित सारे videos देखे, और यह सच में प्रभावशाली है कि उन्होंने design को एक ऐसी चीज़ में समेट दिया जो हर समय Java जैसी ही लगती है
साथ ही उन्होंने value types का मतलब क्या है, और कौन-से optimizations कहां किए जा सकते हैं, इसे कहीं ज़्यादा सूक्ष्मता से खंगाला है
valueजोड़ने भर की हैvalue class में
==असल मेंmemcmp()की तरह काम करने लगेगायह थोड़ा निराशाजनक है, क्योंकि इससे encapsulation टूटता है और implementation details बाहर आ जाती हैं
client code इस आधार पर branch कर सकता है कि दिया गया value अंदरूनी रूप से कैसे represent किया गया है। कुछ मायनों में यह identity comparison से भी बदतर है, क्योंकि identity comparison कम-से-कम internal state को expose नहीं करता
यह classic object orientation को नए तरीके से करने की बात नहीं है, बल्कि object-oriented विचारधारा से पैदा हुई भाषा का post-object-oriented दुनिया की ओर एक और कदम है
मुझे लगता है Java पक्ष में comparison से padding को बाहर रखने या padding bytes को 0 पर force करने जैसी बातों पर ज़रूर विचार किया गया होगा
यह string पर भी काम करना चाहिए। string साफ़ तौर पर heap पर allocate होती रहेगी, और नए “struct” के अंदर pointer का
memcmpकरना ठीक-ठीक identity comparison ही हैJava object identity check और equality check को अलग रखता है।
==मूल रूप से देखता है कि क्या दो pointer एक ही हैं, जबकि equalityequals/hashCodeजैसे interfaces पर आधारित एक subjective अवधारणा हैइसलिए
new Integer(1000) == new Integer(1000)पहलेfalseहोता था, लेकिन अबtrueहोगा, औरnew Integer(1000).equals(new Integer(1000))trueहै, जबकिnew Integer(10) == new Long(10)पहलेfalseथा लेकिन अब compile error बन जाता हैपुराने Java में किसी निश्चित value से नीचे के integer को canonicalized type से replace किया जाता था, शायद 128 के आसपास, इसलिए 10 और 1000 में फ़र्क आता था
अब लगता है कि ऊपर की comparisons में implicitly unboxing हो रही है।
Integer/Longcomparison का पहलेfalseहोना और अब compile error होना साफ़ दिखाता है कि unboxing बीच में आ रही हैvariables के साथ शायद अब भी पुराना behavior मिल सके
खैर, value class जब identity खो देती है, तो
==pointer equality से bitwise equality में बदल जाता है। उम्मीद है ये तमाम corner cases सुलझाए जाएंगे, लेकिन तकनीकी रूप से यह breaking change हैvalue class का उद्देश्य समझ में आता है, लेकिन implementation में खामी है
यह code क्या print करेगा?
Point a = new Point(10, 10); Point b = a; a.x = 100; System.out.println(b.x);अभी तक इसका जवाब साफ़ था, लेकिन value class जुड़ने पर जवाब इस बात पर निर्भर करेगा कि
Pointvalue class है या reference class। इसलिए यह design readability को नुकसान पहुँचाता हैयह uniformity principle का उल्लंघन है। Weinberg की 『The Psychology of Computer Programming』 में uniformity को एक मनोवैज्ञानिक सिद्धांत के रूप में समझाया गया है, जिसके अनुसार उपयोगकर्ता उम्मीद करते हैं कि जो चीज़ें एक जैसी दिखती हैं वे एक जैसा व्यवहार करेंगी, और जो अलग दिखती हैं वे अलग व्यवहार करेंगी
अगर कोई programming language use site पर लगभग एक जैसे दिखने वाले दो syntaxes को अर्थ की दृष्टि से अलग behavior देने लगे, तो reader पर cognitive load बढ़ जाता है। assignment, equality, identity और mutation सामान्य reference object की तरह काम कर रहे हैं या value की तरह, यह जानने के लिए type declaration देखनी पड़ेगी या tools पर निर्भर होना पड़ेगा
अगर declaration के साथ-साथ use site पर भी
valuekeyword ज़रूरी किया गया होता, तो इसे ठीक किया जा सकता था। जैसेvalue Point a = new Point(10, 10);जैसा कुछhttps://openjdk.org/jeps/401
finalहोते हैंबेशक, सिर्फ़ उन चार lines को देखकर यह जानने का कोई तरीका नहीं है, लेकिन यह समस्या अभी भी है।
Pointrecord हो तब भी यही बात लागू होती हैa.x = 100;statement valid नहीं होगा। record type immutable होती हैइसलिए जिस स्थिति की चिंता की जा रही है, वह संभव नहीं होनी चाहिए
अगर इसे
value Point a = new Point(10, 10); value Point b copy= a; a.x uniq= 100; System.out.println(b.x);की तरह लिखा जाता, तो यह ज़्यादा स्पष्ट होता कि clone/copy हो रही है और field change किसी दूसरे object के field को प्रभावित नहीं करतामुझे पता है कि Java दुनिया में .NET के अस्तित्व को मानना शायद असभ्यता समझा जाता है, लेकिन यह .NET struct से कैसे अलग है, यह जानने की जिज्ञासा है
value types, generic specialization और boxing को ऊपर-ऊपर से देखने पर लगता है कि लगभग वही choices की गई हैं
अगर C# ने lower-level नज़रिए से C को लगभग copy किया, तो Java पक्ष ने higher level से जाकर यह बारीकी से विश्लेषण किया कि कौन-सी पाबंदियाँ कौन-से फायदे देती हैं
दूसरी भाषाओं में struct/class का वर्गीकरण binary होता है, लेकिन Java underlying domain के semantics को reflect करते हुए अधिक सूक्ष्म control देता है
और यह भी सामने आया है कि structs में, खासकर parallel context में, तरह-तरह की footguns होती हैं
निजी तौर पर मैं C/C# के structs को mutable और copy द्वारा pass होने वाला मानता हूँ, जबकि value classes immutable हैं और value के रूप में pass होती हैं
मुझे नहीं लगता कि Java में stack allocation की जा सकती है
“C# structs में identity और mutation होती है, इसलिए assignment या passing के समय copy semantics को सटीक रूप से परिभाषित करना पड़ता है, और इस कारण programmer के लिए मॉडल भारी हो जाता है तथा runtime के लिए स्वतंत्रता कम हो जाती है” जैसी झूठी द्वैधता यहाँ ठीक से मेल नहीं खाती
Java class reference semantics वाली identity तो शायद नहीं होगी, लेकिन किसी निश्चित address पर मौजूद unique memory layout के अर्थ में identity तो स्पष्ट रूप से फिर भी रहती है। यह ज़्यादा Java terminology पर शब्द-खेल जैसा लगता है
footnote 6 में “C# के struct से यह कैसे अलग है” वाली बात सटीक नहीं है
लेख में AI-generated images बहुत भरी पड़ी हैं, इसलिए लगता है कि लेखन या कम-से-कम शोध प्रक्रिया में भी काफ़ी hallucination घुसी हुई है
लेख थोड़ा धुंधला और नाटकीय है, लेकिन अच्छी बात यह है कि मूल दस्तावेज़ काफ़ी पढ़ने योग्य हैं
सबसे ऊपर का पेज: https://openjdk.org/projects/jdk/28/spec/
JEP स्थिति: https://bugs.openjdk.org/secure/Dashboard.jspa?selectPageId=...
अच्छा होगा अगर कोई C#, Swift, Java, और Rust में हो रही संबंधित प्रगति को ट्रैक करे। मुझे लगता है कि ये सभी hardware के साथ तालमेल बैठाने की दौड़ में रहे हैं और एक-दूसरे को प्रभावित भी कर रहे हैं
व्यक्तिगत रूप से, मुझे इस बात की चिंता है कि इन बदलावों का FFI memory sharing पर क्या असर पड़ेगा