From f24c55eab68179b31da31ea39d7d821d151bafc3 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Tue, 5 May 2026 16:24:26 +0200 Subject: [PATCH 1/3] JDK-8383559 --- src/hotspot/share/ci/ciField.cpp | 4 +- src/hotspot/share/opto/type.cpp | 2 +- src/hotspot/share/runtime/deoptimization.cpp | 5 --- .../share/classes/java/lang/Boolean.java | 5 +-- .../share/classes/java/lang/Byte.java | 42 ++++++++--------- .../share/classes/java/lang/Character.java | 39 ++++++++-------- .../share/classes/java/lang/Integer.java | 45 ++++++++++--------- .../share/classes/java/lang/Long.java | 43 +++++++++--------- .../share/classes/java/lang/Short.java | 45 ++++++++++--------- 9 files changed, 118 insertions(+), 112 deletions(-) diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp index 08e7db9523a..34f70f5fdd8 100644 --- a/src/hotspot/share/ci/ciField.cpp +++ b/src/hotspot/share/ci/ciField.cpp @@ -499,9 +499,7 @@ bool ciField::is_call_site_target() { bool ciField::is_autobox_cache() { ciSymbol* klass_name = holder()->name(); - // The box cache is disabled when boxes are value classes. - return (!Arguments::is_valhalla_enabled() && - name() == ciSymbols::cache_field_name() && + return (name() == ciSymbols::cache_field_name() && holder()->uses_default_loader() && (klass_name == ciSymbols::java_lang_Character_CharacterCache() || klass_name == ciSymbols::java_lang_Byte_ByteCache() || diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 86eaec50380..39d01e8ac66 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -327,7 +327,7 @@ const Type* Type::make_from_constant(ciConstant constant, bool require_constant, } else { guarantee(require_constant || oop_constant->should_be_constant(), "con_type must get computed"); con_type = TypeOopPtr::make_from_constant(oop_constant, require_constant); - if (Compile::current()->eliminate_boxing() && is_autobox_cache) { + if (is_autobox_cache) { con_type = con_type->is_aryptr()->cast_to_autobox_cache(); } if (stable_dimension > 0) { diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index dea607dd626..b5b053c3f35 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -1146,7 +1146,6 @@ template class Box protected: static BoxCache *_singleton; BoxCache(Thread* thread) { - assert(!Arguments::is_valhalla_enabled(), "Should not use box caches with enable preview"); InstanceKlass* ik = BoxCacheBase::find_cache_klass(thread, CacheType::symbol()); if (ik->is_in_error_state()) { _low = 1; @@ -1256,10 +1255,6 @@ class BooleanBoxCache : public BoxCacheBase { BooleanBoxCache* BooleanBoxCache::_singleton = nullptr; oop Deoptimization::get_cached_box(AutoBoxObjectValue* bv, frame* fr, RegisterMap* reg_map, bool& cache_init_error, TRAPS) { - if (Arguments::enable_preview()) { - // Box caches are not used with enable preview. - return nullptr; - } Klass* k = java_lang_Class::as_Klass(bv->klass()->as_ConstantOopReadValue()->value()()); BasicType box_type = vmClasses::box_klass_type(k); if (box_type != T_OBJECT) { diff --git a/src/java.base/share/classes/java/lang/Boolean.java b/src/java.base/share/classes/java/lang/Boolean.java index 50b4dd05cb7..62ddda35f5e 100644 --- a/src/java.base/share/classes/java/lang/Boolean.java +++ b/src/java.base/share/classes/java/lang/Boolean.java @@ -183,10 +183,7 @@ public boolean booleanValue() { @IntrinsicCandidate @DeserializeConstructor public static Boolean valueOf(boolean b) { - if (!PreviewFeatures.isEnabled()) { - return (b ? TRUE : FALSE); - } - return new Boolean(b); + return (b ? TRUE : FALSE); } /** diff --git a/src/java.base/share/classes/java/lang/Byte.java b/src/java.base/share/classes/java/lang/Byte.java index 79386c3764a..98fa128b167 100644 --- a/src/java.base/share/classes/java/lang/Byte.java +++ b/src/java.base/share/classes/java/lang/Byte.java @@ -28,6 +28,7 @@ import jdk.internal.misc.CDS; import jdk.internal.misc.PreviewFeatures; import jdk.internal.value.DeserializeConstructor; +import jdk.internal.value.ValueClass; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -123,25 +124,29 @@ private ByteCache() {} static Byte[] archivedCache; static { - if (!PreviewFeatures.isEnabled()) { - final int size = -(-128) + 127 + 1; + final int size = -(-128) + 127 + 1; - // Load and use the archived cache if it exists + // Load and use the archived cache if it exists + if (!PreviewFeatures.isEnabled()) { CDS.initializeFromArchive(ByteCache.class); - if (archivedCache == null) { - Byte[] c = new Byte[size]; - byte value = (byte)-128; - for(int i = 0; i < size; i++) { - c[i] = new Byte(value++); - } - archivedCache = c; + } + if (archivedCache == null) { + Byte[] c = newCacheArray(size); + byte value = (byte)-128; + for(int i = 0; i < size; i++) { + c[i] = new Byte(value++); } - cache = archivedCache; - assert cache.length == size; - } else { - cache = null; - assert archivedCache == null; + archivedCache = c; } + cache = archivedCache; + assert cache.length == size; + } + + private static Byte[] newCacheArray(int size) { + if (PreviewFeatures.isEnabled()) { + return (Byte[]) ValueClass.newReferenceArray(Byte.class, size); + } + return new Byte[size]; } } @@ -173,11 +178,8 @@ private ByteCache() {} @IntrinsicCandidate @DeserializeConstructor public static Byte valueOf(byte b) { - if (!PreviewFeatures.isEnabled()) { - final int offset = 128; - return ByteCache.cache[(int) b + offset]; - } - return new Byte(b); + final int offset = 128; + return ByteCache.cache[(int) b + offset]; } /** diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index f6865170be0..c04faebda74 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -28,6 +28,7 @@ import jdk.internal.misc.CDS; import jdk.internal.misc.PreviewFeatures; import jdk.internal.value.DeserializeConstructor; +import jdk.internal.value.ValueClass; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -9435,24 +9436,28 @@ private CharacterCache(){} static Character[] archivedCache; static { - if (!PreviewFeatures.isEnabled()) { - int size = 127 + 1; + int size = 127 + 1; - // Load and use the archived cache if it exists + // Load and use the archived cache if it exists + if (!PreviewFeatures.isEnabled()) { CDS.initializeFromArchive(CharacterCache.class); - if (archivedCache == null) { - Character[] c = new Character[size]; - for (int i = 0; i < size; i++) { - c[i] = new Character((char) i); - } - archivedCache = c; + } + if (archivedCache == null) { + Character[] c = newCacheArray(size); + for (int i = 0; i < size; i++) { + c[i] = new Character((char) i); } - cache = archivedCache; - assert cache.length == size; - } else { - cache = null; - assert archivedCache == null; + archivedCache = c; } + cache = archivedCache; + assert cache.length == size; + } + + private static Character[] newCacheArray(int size) { + if (PreviewFeatures.isEnabled()) { + return (Character[]) ValueClass.newReferenceArray(Character.class, size); + } + return new Character[size]; } } @@ -9487,10 +9492,8 @@ private CharacterCache(){} @IntrinsicCandidate @DeserializeConstructor public static Character valueOf(char c) { - if (!PreviewFeatures.isEnabled()) { - if (c <= 127) { // must cache - return CharacterCache.cache[(int) c]; - } + if (c <= 127) { // must cache + return CharacterCache.cache[(int) c]; } return new Character(c); } diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index bbc4727f857..eef60b06e5b 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -30,6 +30,7 @@ import jdk.internal.misc.VM; import jdk.internal.util.DecimalDigits; import jdk.internal.value.DeserializeConstructor; +import jdk.internal.value.ValueClass; import jdk.internal.vm.annotation.AOTRuntimeSetup; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; @@ -913,12 +914,7 @@ private static final class IntegerCache { static Integer[] archivedCache; static { - if (!PreviewFeatures.isEnabled()) { - runtimeSetup(); - } else { - cache = null; - assert archivedCache == null; - } + runtimeSetup(); } @AOTRuntimeSetup @@ -939,18 +935,22 @@ private static void runtimeSetup() { high = h; Integer[] precomputed = null; - if (cache != null) { - // IntegerCache has been AOT-initialized. - precomputed = cache; - } else { - // Legacy CDS archive support (to be deprecated): - // Load IntegerCache.archivedCache from archive, if possible - CDS.initializeFromArchive(IntegerCache.class); - precomputed = archivedCache; + if (!PreviewFeatures.isEnabled()) { + if (cache != null) { + // IntegerCache has been AOT-initialized. + precomputed = cache; + } else { + // Legacy CDS archive support (to be deprecated): + // Load IntegerCache.archivedCache from archive, if possible + CDS.initializeFromArchive(IntegerCache.class); + precomputed = archivedCache; + } } cache = loadOrInitializeCache(precomputed); - archivedCache = cache; // Legacy CDS archive support (to be deprecated) + if (!PreviewFeatures.isEnabled()) { + archivedCache = cache; // Legacy CDS archive support (to be deprecated) + } // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } @@ -963,7 +963,7 @@ private static Integer[] loadOrInitializeCache(Integer[] precomputed) { return precomputed; } - Integer[] c = new Integer[size]; + Integer[] c = newCacheArray(size); int j = low; // If we loading a precomputed cache (from AOT cache or CDS archive), // we must use all instances from it. @@ -982,6 +982,13 @@ private static Integer[] loadOrInitializeCache(Integer[] precomputed) { return c; } + private static Integer[] newCacheArray(int size) { + if (PreviewFeatures.isEnabled()) { + return (Integer[]) ValueClass.newReferenceArray(Integer.class, size); + } + return new Integer[size]; + } + private IntegerCache() {} } @@ -1015,10 +1022,8 @@ private IntegerCache() {} @IntrinsicCandidate @DeserializeConstructor public static Integer valueOf(int i) { - if (!PreviewFeatures.isEnabled()) { - if (i >= IntegerCache.low && i <= IntegerCache.high) - return IntegerCache.cache[i + (-IntegerCache.low)]; - } + if (i >= IntegerCache.low && i <= IntegerCache.high) + return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 5053cdf136a..93d95c1b870 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -36,6 +36,7 @@ import jdk.internal.misc.CDS; import jdk.internal.misc.PreviewFeatures; import jdk.internal.value.DeserializeConstructor; +import jdk.internal.value.ValueClass; import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; @@ -933,25 +934,29 @@ private LongCache() {} static Long[] archivedCache; static { - if (!PreviewFeatures.isEnabled()) { - int size = -(-128) + 127 + 1; + int size = -(-128) + 127 + 1; - // Load and use the archived cache if it exists + // Load and use the archived cache if it exists + if (!PreviewFeatures.isEnabled()) { CDS.initializeFromArchive(LongCache.class); - if (archivedCache == null) { - Long[] c = new Long[size]; - long value = -128; - for(int i = 0; i < size; i++) { - c[i] = new Long(value++); - } - archivedCache = c; + } + if (archivedCache == null) { + Long[] c = newCacheArray(size); + long value = -128; + for(int i = 0; i < size; i++) { + c[i] = new Long(value++); } - cache = archivedCache; - assert cache.length == size; - } else { - cache = null; - assert archivedCache == null; + archivedCache = c; } + cache = archivedCache; + assert cache.length == size; + } + + private static Long[] newCacheArray(int size) { + if (PreviewFeatures.isEnabled()) { + return (Long[]) ValueClass.newReferenceArray(Long.class, size); + } + return new Long[size]; } } @@ -985,11 +990,9 @@ private LongCache() {} @IntrinsicCandidate @DeserializeConstructor public static Long valueOf(long l) { - if (!PreviewFeatures.isEnabled()) { - if (l >= -128 && l <= 127) { // will cache - final int offset = 128; - return LongCache.cache[(int) l + offset]; - } + if (l >= -128 && l <= 127) { // will cache + final int offset = 128; + return LongCache.cache[(int) l + offset]; } return new Long(l); } diff --git a/src/java.base/share/classes/java/lang/Short.java b/src/java.base/share/classes/java/lang/Short.java index 75066903a51..c07b64d3540 100644 --- a/src/java.base/share/classes/java/lang/Short.java +++ b/src/java.base/share/classes/java/lang/Short.java @@ -28,6 +28,7 @@ import jdk.internal.misc.CDS; import jdk.internal.misc.PreviewFeatures; import jdk.internal.value.DeserializeConstructor; +import jdk.internal.value.ValueClass; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -250,25 +251,29 @@ private ShortCache() {} static Short[] archivedCache; static { - if (!PreviewFeatures.isEnabled()) { - int size = -(-128) + 127 + 1; + int size = -(-128) + 127 + 1; - // Load and use the archived cache if it exists + // Load and use the archived cache if it exists + if (!PreviewFeatures.isEnabled()) { CDS.initializeFromArchive(ShortCache.class); - if (archivedCache == null) { - Short[] c = new Short[size]; - short value = -128; - for(int i = 0; i < size; i++) { - c[i] = new Short(value++); - } - archivedCache = c; + } + if (archivedCache == null) { + Short[] c = newCacheArray(size); + short value = -128; + for(int i = 0; i < size; i++) { + c[i] = new Short(value++); } - cache = archivedCache; - assert cache.length == size; - } else { - cache = null; - assert archivedCache == null; + archivedCache = c; } + cache = archivedCache; + assert cache.length == size; + } + + private static Short[] newCacheArray(int size) { + if (PreviewFeatures.isEnabled()) { + return (Short[]) ValueClass.newReferenceArray(Short.class, size); + } + return new Short[size]; } } @@ -302,12 +307,10 @@ private ShortCache() {} @IntrinsicCandidate @DeserializeConstructor public static Short valueOf(short s) { - if (!PreviewFeatures.isEnabled()) { - final int offset = 128; - int sAsInt = s; - if (sAsInt >= -128 && sAsInt <= 127) { // must cache - return ShortCache.cache[sAsInt + offset]; - } + final int offset = 128; + int sAsInt = s; + if (sAsInt >= -128 && sAsInt <= 127) { // must cache + return ShortCache.cache[sAsInt + offset]; } return new Short(s); } From 88b9e2f28be6eb8ced8b9b54cf4c9742c80c99d6 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Wed, 6 May 2026 10:52:30 +0200 Subject: [PATCH 2/3] Update src/hotspot/share/opto/type.cpp --- src/hotspot/share/opto/type.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 39d01e8ac66..86eaec50380 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -327,7 +327,7 @@ const Type* Type::make_from_constant(ciConstant constant, bool require_constant, } else { guarantee(require_constant || oop_constant->should_be_constant(), "con_type must get computed"); con_type = TypeOopPtr::make_from_constant(oop_constant, require_constant); - if (is_autobox_cache) { + if (Compile::current()->eliminate_boxing() && is_autobox_cache) { con_type = con_type->is_aryptr()->cast_to_autobox_cache(); } if (stable_dimension > 0) { From d51e0ad0768c0238de20a01ff5be8fca8a2b162a Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Wed, 13 May 2026 12:45:06 +0200 Subject: [PATCH 3/3] Reviewer comments --- src/hotspot/share/runtime/deoptimization.cpp | 8 +++-- .../share/classes/java/lang/Boolean.java | 32 +++++++++++++------ .../share/classes/java/lang/Byte.java | 7 ++-- .../share/classes/java/lang/Character.java | 7 ++-- .../share/classes/java/lang/Double.java | 25 +++++++++++---- .../share/classes/java/lang/Float.java | 25 +++++++++++---- .../share/classes/java/lang/Integer.java | 27 +++++++--------- .../share/classes/java/lang/Long.java | 7 ++-- .../share/classes/java/lang/Short.java | 9 +++--- .../cacheObject/ArchivedIntegerCacheTest.java | 1 - 10 files changed, 89 insertions(+), 59 deletions(-) diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index b5b053c3f35..3806aa53350 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -1153,6 +1153,7 @@ template class Box _cache = nullptr; } else { refArrayOop cache = CacheType::cache(ik); + assert(!cache->is_flatArray(), "box caches must be reference arrays"); assert(cache->length() > 0, "Empty cache"); _low = BoxType::value(cache->obj_at(0)); _high = checked_cast(_low + cache->length() - 1); @@ -1217,8 +1218,11 @@ class BooleanBoxCache : public BoxCacheBase { _true_cache = nullptr; _false_cache = nullptr; } else { - _true_cache = JNIHandles::make_global(Handle(thread, java_lang_Boolean::get_TRUE(ik))); - _false_cache = JNIHandles::make_global(Handle(thread, java_lang_Boolean::get_FALSE(ik))); + oop true_cache = java_lang_Boolean::get_TRUE(ik); + oop false_cache = java_lang_Boolean::get_FALSE(ik); + assert(true_cache != nullptr && false_cache != nullptr, "Boolean cache fields must be initialized"); + _true_cache = JNIHandles::make_global(Handle(thread, true_cache)); + _false_cache = JNIHandles::make_global(Handle(thread, false_cache)); } } ~BooleanBoxCache() { diff --git a/src/java.base/share/classes/java/lang/Boolean.java b/src/java.base/share/classes/java/lang/Boolean.java index 62ddda35f5e..8e471963a24 100644 --- a/src/java.base/share/classes/java/lang/Boolean.java +++ b/src/java.base/share/classes/java/lang/Boolean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ package java.lang; -import jdk.internal.misc.PreviewFeatures; import jdk.internal.value.DeserializeConstructor; import jdk.internal.vm.annotation.IntrinsicCandidate; @@ -112,6 +111,7 @@ public final class Boolean implements java.io.Serializable, * if possible. */ @Deprecated(since="9") + @DeserializeConstructor public Boolean(boolean value) { this.value = value; } @@ -168,20 +168,32 @@ public boolean booleanValue() { /** * Returns a {@code Boolean} instance representing the specified - * {@code boolean} value. If the specified {@code boolean} value - * is {@code true}, this method returns {@code Boolean.TRUE}; - * if it is {@code false}, this method returns {@code Boolean.FALSE}. - * If a new {@code Boolean} instance is not required, this method - * should generally be used in preference to the constructor - * {@link #Boolean(boolean)}, as this method is likely to yield - * significantly better space and time performance. + * {@code boolean} value. + *
+ *
+ *

+ * - When preview features are NOT enabled, {@code Boolean} is an identity class. + * If the specified {@code boolean} value is {@code true}, + * this method returns {@code Boolean.TRUE}; if it is + * {@code false}, this method returns {@code Boolean.FALSE}. + * If a new {@code Boolean} instance is not required, this + * method should generally be used in preference to the + * constructor {@link #Boolean(boolean)}, as this method is + * likely to yield significantly better space and time + * performance. + *

+ *

+ * - When preview features are enabled, {@code Boolean} is a {@linkplain Class#isValue value class}. + * The {@code valueOf} behavior is the same as invoking the constructor. + *

+ *
+ *
* * @param b a boolean value. * @return a {@code Boolean} instance representing {@code b}. * @since 1.4 */ @IntrinsicCandidate - @DeserializeConstructor public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); } diff --git a/src/java.base/share/classes/java/lang/Byte.java b/src/java.base/share/classes/java/lang/Byte.java index 98fa128b167..af44ca334ca 100644 --- a/src/java.base/share/classes/java/lang/Byte.java +++ b/src/java.base/share/classes/java/lang/Byte.java @@ -127,9 +127,7 @@ private ByteCache() {} final int size = -(-128) + 127 + 1; // Load and use the archived cache if it exists - if (!PreviewFeatures.isEnabled()) { - CDS.initializeFromArchive(ByteCache.class); - } + CDS.initializeFromArchive(ByteCache.class); if (archivedCache == null) { Byte[] c = newCacheArray(size); byte value = (byte)-128; @@ -143,6 +141,7 @@ private ByteCache() {} } private static Byte[] newCacheArray(int size) { + // ValueClass.newReferenceArray requires a value class component. if (PreviewFeatures.isEnabled()) { return (Byte[]) ValueClass.newReferenceArray(Byte.class, size); } @@ -176,7 +175,6 @@ private static Byte[] newCacheArray(int size) { * @since 1.5 */ @IntrinsicCandidate - @DeserializeConstructor public static Byte valueOf(byte b) { final int offset = 128; return ByteCache.cache[(int) b + offset]; @@ -380,6 +378,7 @@ public static Byte decode(String nm) throws NumberFormatException { * likely to yield significantly better space and time performance. */ @Deprecated(since="9") + @DeserializeConstructor public Byte(byte value) { this.value = value; } diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index c04faebda74..1dddbd5948e 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -9423,6 +9423,7 @@ public static final UnicodeScript forName(String scriptName) { * likely to yield significantly better space and time performance. */ @Deprecated(since="9") + @DeserializeConstructor public Character(char value) { this.value = value; } @@ -9439,9 +9440,7 @@ private CharacterCache(){} int size = 127 + 1; // Load and use the archived cache if it exists - if (!PreviewFeatures.isEnabled()) { - CDS.initializeFromArchive(CharacterCache.class); - } + CDS.initializeFromArchive(CharacterCache.class); if (archivedCache == null) { Character[] c = newCacheArray(size); for (int i = 0; i < size; i++) { @@ -9454,6 +9453,7 @@ private CharacterCache(){} } private static Character[] newCacheArray(int size) { + // ValueClass.newReferenceArray requires a value class component. if (PreviewFeatures.isEnabled()) { return (Character[]) ValueClass.newReferenceArray(Character.class, size); } @@ -9490,7 +9490,6 @@ private static Character[] newCacheArray(int size) { * @since 1.5 */ @IntrinsicCandidate - @DeserializeConstructor public static Character valueOf(char c) { if (c <= 127) { // must cache return CharacterCache.cache[(int) c]; diff --git a/src/java.base/share/classes/java/lang/Double.java b/src/java.base/share/classes/java/lang/Double.java index 046d868b9fd..29a84d54d61 100644 --- a/src/java.base/share/classes/java/lang/Double.java +++ b/src/java.base/share/classes/java/lang/Double.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -968,18 +968,28 @@ public static Double valueOf(String s) throws NumberFormatException { /** * Returns a {@code Double} instance representing the specified * {@code double} value. - * If a new {@code Double} instance is not required, this method - * should generally be used in preference to the constructor - * {@link #Double(double)}, as this method is likely to yield - * significantly better space and time performance by caching - * frequently requested values. + *
+ *
+ *

+ * - When preview features are NOT enabled, {@code Double} is an identity class. + * If a new {@code Double} instance is not required, this + * method should generally be used in preference to the + * constructor {@link #Double(double)}, as this method is + * likely to yield significantly better space and time + * performance by caching frequently requested values. + *

+ *

+ * - When preview features are enabled, {@code Double} is a {@linkplain Class#isValue value class}. + * The {@code valueOf} behavior is the same as invoking the constructor. + *

+ *
+ *
* * @param d a double value. * @return a {@code Double} instance representing {@code d}. * @since 1.5 */ @IntrinsicCandidate - @DeserializeConstructor public static Double valueOf(double d) { return new Double(d); } @@ -1075,6 +1085,7 @@ public static boolean isFinite(double d) { * likely to yield significantly better space and time performance. */ @Deprecated(since="9") + @DeserializeConstructor public Double(double value) { this.value = value; } diff --git a/src/java.base/share/classes/java/lang/Float.java b/src/java.base/share/classes/java/lang/Float.java index cb5a15b5fcb..402688f058a 100644 --- a/src/java.base/share/classes/java/lang/Float.java +++ b/src/java.base/share/classes/java/lang/Float.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -571,18 +571,28 @@ public static Float valueOf(String s) throws NumberFormatException { /** * Returns a {@code Float} instance representing the specified * {@code float} value. - * If a new {@code Float} instance is not required, this method - * should generally be used in preference to the constructor - * {@link #Float(float)}, as this method is likely to yield - * significantly better space and time performance by caching - * frequently requested values. + *
+ *
+ *

+ * - When preview features are NOT enabled, {@code Float} is an identity class. + * If a new {@code Float} instance is not required, this + * method should generally be used in preference to the + * constructor {@link #Float(float)}, as this method is + * likely to yield significantly better space and time + * performance by caching frequently requested values. + *

+ *

+ * - When preview features are enabled, {@code Float} is a {@linkplain Class#isValue value class}. + * The {@code valueOf} behavior is the same as invoking the constructor. + *

+ *
+ *
* * @param f a float value. * @return a {@code Float} instance representing {@code f}. * @since 1.5 */ @IntrinsicCandidate - @DeserializeConstructor public static Float valueOf(float f) { return new Float(f); } @@ -678,6 +688,7 @@ public static boolean isFinite(float f) { * likely to yield significantly better space and time performance. */ @Deprecated(since="9") + @DeserializeConstructor public Float(float value) { this.value = value; } diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index eef60b06e5b..a31ed449e70 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -934,23 +934,19 @@ private static void runtimeSetup() { } high = h; - Integer[] precomputed = null; - if (!PreviewFeatures.isEnabled()) { - if (cache != null) { - // IntegerCache has been AOT-initialized. - precomputed = cache; - } else { - // Legacy CDS archive support (to be deprecated): - // Load IntegerCache.archivedCache from archive, if possible - CDS.initializeFromArchive(IntegerCache.class); - precomputed = archivedCache; - } + Integer[] precomputed; + if (cache != null) { + // IntegerCache has been AOT-initialized. + precomputed = cache; + } else { + // Legacy CDS archive support (to be deprecated): + // Load IntegerCache.archivedCache from archive, if possible + CDS.initializeFromArchive(IntegerCache.class); + precomputed = archivedCache; } cache = loadOrInitializeCache(precomputed); - if (!PreviewFeatures.isEnabled()) { - archivedCache = cache; // Legacy CDS archive support (to be deprecated) - } + archivedCache = cache; // Legacy CDS archive support (to be deprecated) // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } @@ -983,6 +979,7 @@ private static Integer[] loadOrInitializeCache(Integer[] precomputed) { } private static Integer[] newCacheArray(int size) { + // ValueClass.newReferenceArray requires a value class component. if (PreviewFeatures.isEnabled()) { return (Integer[]) ValueClass.newReferenceArray(Integer.class, size); } @@ -1020,7 +1017,6 @@ private IntegerCache() {} * @since 1.5 */ @IntrinsicCandidate - @DeserializeConstructor public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; @@ -1047,6 +1043,7 @@ public static Integer valueOf(int i) { * likely to yield significantly better space and time performance. */ @Deprecated(since="9") + @DeserializeConstructor public Integer(int value) { this.value = value; } diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 93d95c1b870..198d743c093 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -937,9 +937,7 @@ private LongCache() {} int size = -(-128) + 127 + 1; // Load and use the archived cache if it exists - if (!PreviewFeatures.isEnabled()) { - CDS.initializeFromArchive(LongCache.class); - } + CDS.initializeFromArchive(LongCache.class); if (archivedCache == null) { Long[] c = newCacheArray(size); long value = -128; @@ -953,6 +951,7 @@ private LongCache() {} } private static Long[] newCacheArray(int size) { + // ValueClass.newReferenceArray requires a value class component. if (PreviewFeatures.isEnabled()) { return (Long[]) ValueClass.newReferenceArray(Long.class, size); } @@ -988,7 +987,6 @@ private static Long[] newCacheArray(int size) { * @since 1.5 */ @IntrinsicCandidate - @DeserializeConstructor public static Long valueOf(long l) { if (l >= -128 && l <= 127) { // will cache final int offset = 128; @@ -1107,6 +1105,7 @@ else if (nm.startsWith("0", index) && nm.length() > 1 + index) { * likely to yield significantly better space and time performance. */ @Deprecated(since="9") + @DeserializeConstructor public Long(long value) { this.value = value; } diff --git a/src/java.base/share/classes/java/lang/Short.java b/src/java.base/share/classes/java/lang/Short.java index c07b64d3540..f08a64e0a04 100644 --- a/src/java.base/share/classes/java/lang/Short.java +++ b/src/java.base/share/classes/java/lang/Short.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -254,9 +254,7 @@ private ShortCache() {} int size = -(-128) + 127 + 1; // Load and use the archived cache if it exists - if (!PreviewFeatures.isEnabled()) { - CDS.initializeFromArchive(ShortCache.class); - } + CDS.initializeFromArchive(ShortCache.class); if (archivedCache == null) { Short[] c = newCacheArray(size); short value = -128; @@ -270,6 +268,7 @@ private ShortCache() {} } private static Short[] newCacheArray(int size) { + // ValueClass.newReferenceArray requires a value class component. if (PreviewFeatures.isEnabled()) { return (Short[]) ValueClass.newReferenceArray(Short.class, size); } @@ -305,7 +304,6 @@ private static Short[] newCacheArray(int size) { * @since 1.5 */ @IntrinsicCandidate - @DeserializeConstructor public static Short valueOf(short s) { final int offset = 128; int sAsInt = s; @@ -385,6 +383,7 @@ public static Short decode(String nm) throws NumberFormatException { * likely to yield significantly better space and time performance. */ @Deprecated(since="9") + @DeserializeConstructor public Short(short value) { this.value = value; } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java index 9e78298b21c..f69f2be4864 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java @@ -25,7 +25,6 @@ /* * @test * @summary Test primitive box caches integrity in various scenarios (IntegerCache etc) - * @requires !java.enablePreview * @requires vm.cds.write.archived.java.heap * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @compile --add-exports java.base/jdk.internal.misc=ALL-UNNAMED CheckIntegerCacheApp.java ArchivedIntegerHolder.java