From acd72b826217a61636024fc2df56155f6d36482d Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Tue, 21 Jan 2025 15:38:54 +0100 Subject: [PATCH 1/5] Backport 8309622: Re-examine the cache mechanism in BaseLocale --- .../share/classes/java/util/Locale.java | 31 +-- .../classes/java/util/ResourceBundle.java | 189 ++++++++------- .../classes/sun/util/locale/BaseLocale.java | 222 +++++------------- .../sun/util/locale/LocaleObjectCache.java | 114 --------- 4 files changed, 166 insertions(+), 390 deletions(-) delete mode 100644 src/java.base/share/classes/sun/util/locale/LocaleObjectCache.java diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 02f556489e6..66cdc112df0 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, 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 @@ -50,6 +50,7 @@ import java.util.spi.LocaleNameProvider; import java.util.stream.Stream; +import jdk.internal.util.ReferencedKeyMap; import jdk.internal.util.StaticProperty; import jdk.internal.vm.annotation.Stable; @@ -59,7 +60,6 @@ import sun.util.locale.LanguageTag; import sun.util.locale.LocaleExtensions; import sun.util.locale.LocaleMatcher; -import sun.util.locale.LocaleObjectCache; import sun.util.locale.LocaleSyntaxException; import sun.util.locale.LocaleUtils; import sun.util.locale.ParseStatus; @@ -918,29 +918,20 @@ static Locale getInstance(BaseLocale baseloc, LocaleExtensions extensions) { if (locale != null) { return locale; } - return Cache.LOCALECACHE.get(baseloc); + return LOCALE_CACHE.computeIfAbsent(baseloc, Locale::createLocale); } else { LocaleKey key = new LocaleKey(baseloc, extensions); - return Cache.LOCALECACHE.get(key); + return LOCALE_CACHE.computeIfAbsent(key, Locale::createLocale); } } - private static class Cache extends LocaleObjectCache { - - private static final Cache LOCALECACHE = new Cache(); - - private Cache() { - } - - @Override - protected Locale createObject(Object key) { - if (key instanceof BaseLocale) { - return new Locale((BaseLocale)key, null); - } else { - LocaleKey lk = (LocaleKey)key; - return new Locale(lk.base, lk.exts); - } - } + private static final ReferencedKeyMap LOCALE_CACHE = ReferencedKeyMap.create(true, ConcurrentHashMap::new); + private static Locale createLocale(Object key) { + return switch (key) { + case BaseLocale base -> new Locale(base, null); + case LocaleKey lk -> new Locale(lk.base, lk.exts); + default -> throw new InternalError("should not happen"); + }; } private static final class LocaleKey { diff --git a/src/java.base/share/classes/java/util/ResourceBundle.java b/src/java.base/share/classes/java/util/ResourceBundle.java index b9a2dca88d5..48cd6305246 100644 --- a/src/java.base/share/classes/java/util/ResourceBundle.java +++ b/src/java.base/share/classes/java/util/ResourceBundle.java @@ -69,9 +69,9 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.util.ReferencedKeyMap; import sun.security.action.GetPropertyAction; import sun.util.locale.BaseLocale; -import sun.util.locale.LocaleObjectCache; import sun.util.resources.Bundles; import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION; @@ -2876,123 +2876,122 @@ public List getCandidateLocales(String baseName, Locale locale) { if (baseName == null) { throw new NullPointerException(); } - return new ArrayList<>(CANDIDATES_CACHE.get(locale.getBaseLocale())); + return new ArrayList<>(CANDIDATES_CACHE.computeIfAbsent(locale.getBaseLocale(), + Control::createCandidateList)); } - private static final CandidateListCache CANDIDATES_CACHE = new CandidateListCache(); + private static final ReferencedKeyMap> CANDIDATES_CACHE = ReferencedKeyMap.create(true, ConcurrentHashMap::new); - private static class CandidateListCache extends LocaleObjectCache> { - protected List createObject(BaseLocale base) { - String language = base.getLanguage(); - String script = base.getScript(); - String region = base.getRegion(); - String variant = base.getVariant(); + private static List createCandidateList(BaseLocale base) { + String language = base.getLanguage(); + String script = base.getScript(); + String region = base.getRegion(); + String variant = base.getVariant(); - // Special handling for Norwegian - boolean isNorwegianBokmal = false; - boolean isNorwegianNynorsk = false; - if (language.equals("no")) { - if (region.equals("NO") && variant.equals("NY")) { - variant = ""; - isNorwegianNynorsk = true; - } else { - isNorwegianBokmal = true; - } + // Special handling for Norwegian + boolean isNorwegianBokmal = false; + boolean isNorwegianNynorsk = false; + if (language.equals("no")) { + if (region.equals("NO") && variant.equals("NY")) { + variant = ""; + isNorwegianNynorsk = true; + } else { + isNorwegianBokmal = true; } - if (language.equals("nb") || isNorwegianBokmal) { - List tmpList = getDefaultList("nb", script, region, variant); - // Insert a locale replacing "nb" with "no" for every list entry with precedence - List bokmalList = new ArrayList<>(); - for (Locale l_nb : tmpList) { - var isRoot = l_nb.getLanguage().isEmpty(); - var l_no = Locale.getInstance(isRoot ? "" : "no", - l_nb.getScript(), l_nb.getCountry(), l_nb.getVariant(), null); - bokmalList.add(isNorwegianBokmal ? l_no : l_nb); - if (isRoot) { - break; - } - bokmalList.add(isNorwegianBokmal ? l_nb : l_no); + } + if (language.equals("nb") || isNorwegianBokmal) { + List tmpList = getDefaultList("nb", script, region, variant); + // Insert a locale replacing "nb" with "no" for every list entry with precedence + List bokmalList = new ArrayList<>(); + for (Locale l_nb : tmpList) { + var isRoot = l_nb.getLanguage().isEmpty(); + var l_no = Locale.getInstance(isRoot ? "" : "no", + l_nb.getScript(), l_nb.getCountry(), l_nb.getVariant(), null); + bokmalList.add(isNorwegianBokmal ? l_no : l_nb); + if (isRoot) { + break; } - return bokmalList; - } else if (language.equals("nn") || isNorwegianNynorsk) { - // Insert no_NO_NY, no_NO, no after nn - List nynorskList = getDefaultList("nn", script, region, variant); - int idx = nynorskList.size() - 1; - nynorskList.add(idx++, Locale.getInstance("no", "NO", "NY")); - nynorskList.add(idx++, Locale.getInstance("no", "NO", "")); - nynorskList.add(idx++, Locale.getInstance("no", "", "")); - return nynorskList; + bokmalList.add(isNorwegianBokmal ? l_nb : l_no); } - // Special handling for Chinese - else if (language.equals("zh")) { - if (script.isEmpty() && !region.isEmpty()) { - // Supply script for users who want to use zh_Hans/zh_Hant - // as bundle names (recommended for Java7+) - switch (region) { - case "TW", "HK", "MO" -> script = "Hant"; - case "CN", "SG" -> script = "Hans"; - } + return bokmalList; + } else if (language.equals("nn") || isNorwegianNynorsk) { + // Insert no_NO_NY, no_NO, no after nn + List nynorskList = getDefaultList("nn", script, region, variant); + int idx = nynorskList.size() - 1; + nynorskList.add(idx++, Locale.getInstance("no", "NO", "NY")); + nynorskList.add(idx++, Locale.getInstance("no", "NO", "")); + nynorskList.add(idx++, Locale.getInstance("no", "", "")); + return nynorskList; + } + // Special handling for Chinese + else if (language.equals("zh")) { + if (script.isEmpty() && !region.isEmpty()) { + // Supply script for users who want to use zh_Hans/zh_Hant + // as bundle names (recommended for Java7+) + switch (region) { + case "TW", "HK", "MO" -> script = "Hant"; + case "CN", "SG" -> script = "Hans"; } } - - return getDefaultList(language, script, region, variant); } - private static List getDefaultList(String language, String script, String region, String variant) { - List variants = null; + return getDefaultList(language, script, region, variant); + } - if (!variant.isEmpty()) { - variants = new ArrayList<>(); - int idx = variant.length(); - while (idx != -1) { - variants.add(variant.substring(0, idx)); - idx = variant.lastIndexOf('_', --idx); - } + private static List getDefaultList(String language, String script, String region, String variant) { + List variants = null; + + if (!variant.isEmpty()) { + variants = new ArrayList<>(); + int idx = variant.length(); + while (idx != -1) { + variants.add(variant.substring(0, idx)); + idx = variant.lastIndexOf('_', --idx); } + } - List list = new ArrayList<>(); + List list = new ArrayList<>(); - if (variants != null) { - for (String v : variants) { - list.add(Locale.getInstance(language, script, region, v, null)); - } - } - if (!region.isEmpty()) { - list.add(Locale.getInstance(language, script, region, "", null)); + if (variants != null) { + for (String v : variants) { + list.add(Locale.getInstance(language, script, region, v, null)); } - if (!script.isEmpty()) { - list.add(Locale.getInstance(language, script, "", "", null)); - // Special handling for Chinese - if (language.equals("zh")) { - if (region.isEmpty()) { - // Supply region(country) for users who still package Chinese - // bundles using old convention. - switch (script) { - case "Hans" -> region = "CN"; - case "Hant" -> region = "TW"; - } + } + if (!region.isEmpty()) { + list.add(Locale.getInstance(language, script, region, "", null)); + } + if (!script.isEmpty()) { + list.add(Locale.getInstance(language, script, "", "", null)); + // Special handling for Chinese + if (language.equals("zh")) { + if (region.isEmpty()) { + // Supply region(country) for users who still package Chinese + // bundles using old convention. + switch (script) { + case "Hans" -> region = "CN"; + case "Hant" -> region = "TW"; } } + } - // With script, after truncating variant, region and script, - // start over without script. - if (variants != null) { - for (String v : variants) { - list.add(Locale.getInstance(language, "", region, v, null)); - } - } - if (!region.isEmpty()) { - list.add(Locale.getInstance(language, "", region, "", null)); + // With script, after truncating variant, region and script, + // start over without script. + if (variants != null) { + for (String v : variants) { + list.add(Locale.getInstance(language, "", region, v, null)); } } - if (!language.isEmpty()) { - list.add(Locale.getInstance(language, "", "", "", null)); + if (!region.isEmpty()) { + list.add(Locale.getInstance(language, "", region, "", null)); } - // Add root locale at the end - list.add(Locale.ROOT); - - return list; } + if (!language.isEmpty()) { + list.add(Locale.getInstance(language, "", "", "", null)); + } + // Add root locale at the end + list.add(Locale.ROOT); + + return list; } /** diff --git a/src/java.base/share/classes/sun/util/locale/BaseLocale.java b/src/java.base/share/classes/sun/util/locale/BaseLocale.java index 89cda00a208..7e0fc9a2d34 100644 --- a/src/java.base/share/classes/sun/util/locale/BaseLocale.java +++ b/src/java.base/share/classes/sun/util/locale/BaseLocale.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, 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 @@ -33,34 +33,35 @@ package sun.util.locale; import jdk.internal.misc.CDS; +import jdk.internal.util.ReferencedKeySet; import jdk.internal.util.StaticProperty; import jdk.internal.vm.annotation.Stable; -import java.lang.ref.SoftReference; import java.util.StringJoiner; +import java.util.concurrent.ConcurrentHashMap; public final class BaseLocale { public static @Stable BaseLocale[] constantBaseLocales; - public static final byte ENGLISH = 0, - FRENCH = 1, - GERMAN = 2, - ITALIAN = 3, - JAPANESE = 4, - KOREAN = 5, - CHINESE = 6, - SIMPLIFIED_CHINESE = 7, - TRADITIONAL_CHINESE = 8, - FRANCE = 9, - GERMANY = 10, - ITALY = 11, - JAPAN = 12, - KOREA = 13, - UK = 14, - US = 15, - CANADA = 16, - CANADA_FRENCH = 17, - ROOT = 18, + public static final byte ROOT = 0, + ENGLISH = 1, + US = 2, + FRENCH = 3, + GERMAN = 4, + ITALIAN = 5, + JAPANESE = 6, + KOREAN = 7, + CHINESE = 8, + SIMPLIFIED_CHINESE = 9, + TRADITIONAL_CHINESE = 10, + FRANCE = 11, + GERMANY = 12, + ITALY = 13, + JAPAN = 14, + KOREA = 15, + UK = 16, + CANADA = 17, + CANADA_FRENCH = 18, NUM_CONSTANTS = 19; static { CDS.initializeFromArchive(BaseLocale.class); @@ -90,6 +91,10 @@ public final class BaseLocale { } } + // Interned BaseLocale cache + private static final ReferencedKeySet CACHE = + ReferencedKeySet.create(true, ConcurrentHashMap::new); + public static final String SEP = "_"; private final String language; @@ -107,27 +112,17 @@ public final class BaseLocale { private static final boolean OLD_ISO_CODES = StaticProperty.javaLocaleUseOldISOCodes() .equalsIgnoreCase("true"); - // This method must be called with normalize = false only when creating the - // Locale.* constants and non-normalized BaseLocale$Keys used for lookup. - private BaseLocale(String language, String script, String region, String variant, - boolean normalize) { - if (normalize) { - this.language = LocaleUtils.toLowerString(language).intern(); - this.script = LocaleUtils.toTitleString(script).intern(); - this.region = LocaleUtils.toUpperString(region).intern(); - this.variant = variant.intern(); - } else { - this.language = language; - this.script = script; - this.region = region; - this.variant = variant; - } + private BaseLocale(String language, String script, String region, String variant) { + this.language = language; + this.script = script; + this.region = region; + this.variant = variant; } // Called for creating the Locale.* constants. No argument // validation is performed. private static BaseLocale createInstance(String language, String region) { - return new BaseLocale(language, "", region, "", false); + return new BaseLocale(language, "", region, ""); } public static BaseLocale getInstance(String language, String script, @@ -153,8 +148,8 @@ public static BaseLocale getInstance(String language, String script, // Check for constant base locales first if (script.isEmpty() && variant.isEmpty()) { for (BaseLocale baseLocale : constantBaseLocales) { - if (baseLocale.getLanguage().equals(language) - && baseLocale.getRegion().equals(region)) { + if (baseLocale.language.equals(language) + && baseLocale.region.equals(region)) { return baseLocale; } } @@ -165,8 +160,15 @@ public static BaseLocale getInstance(String language, String script, language = convertOldISOCodes(language); } - Key key = new Key(language, script, region, variant, false); - return Cache.CACHE.get(key); + // Obtain the "interned" BaseLocale from the cache. The returned + // "interned" instance can subsequently be used by the Locale + // instance which guarantees the locale components are properly cased/interned. + return CACHE.intern(new BaseLocale(language, script, region, variant), + (b) -> new BaseLocale( + LocaleUtils.toLowerString(b.language).intern(), + LocaleUtils.toTitleString(b.script).intern(), + LocaleUtils.toUpperString(b.region).intern(), + b.variant.intern())); } public static String convertOldISOCodes(String language) { @@ -199,14 +201,14 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof BaseLocale)) { - return false; + if (obj instanceof BaseLocale other) { + return LocaleUtils.caseIgnoreMatch(other.language, language) + && LocaleUtils.caseIgnoreMatch(other.region, region) + && LocaleUtils.caseIgnoreMatch(other.script, script) + // variant is case sensitive in JDK! + && other.variant.equals(variant); } - BaseLocale other = (BaseLocale)obj; - return language == other.language - && script == other.script - && region == other.region - && variant == other.variant; + return false; } @Override @@ -231,128 +233,26 @@ public String toString() { public int hashCode() { int h = hash; if (h == 0) { - // Generating a hash value from language, script, region and variant - h = language.hashCode(); - h = 31 * h + script.hashCode(); - h = 31 * h + region.hashCode(); - h = 31 * h + variant.hashCode(); - if (h != 0) { - hash = h; - } - } - return h; - } - - private static final class Key { - /** - * Keep a SoftReference to the Key data if normalized (actually used - * as a cache key) and not initialized via the constant creation path. - * - * This allows us to avoid creating SoftReferences on lookup Keys - * (which are short-lived) and for Locales created via - * Locale#createConstant. - */ - private final SoftReference holderRef; - private final BaseLocale holder; - - private final boolean normalized; - private final int hash; - - private Key(String language, String script, String region, - String variant, boolean normalize) { - BaseLocale locale = new BaseLocale(language, script, region, variant, normalize); - this.normalized = normalize; - if (normalized) { - this.holderRef = new SoftReference<>(locale); - this.holder = null; - } else { - this.holderRef = null; - this.holder = locale; - } - this.hash = hashCode(locale); - } - - public int hashCode() { - return hash; - } - - private int hashCode(BaseLocale locale) { - int h = 0; - String lang = locale.getLanguage(); - int len = lang.length(); + int len = language.length(); for (int i = 0; i < len; i++) { - h = 31*h + LocaleUtils.toLower(lang.charAt(i)); + h = 31*h + LocaleUtils.toLower(language.charAt(i)); } - String scrt = locale.getScript(); - len = scrt.length(); + len = script.length(); for (int i = 0; i < len; i++) { - h = 31*h + LocaleUtils.toLower(scrt.charAt(i)); + h = 31*h + LocaleUtils.toLower(script.charAt(i)); } - String regn = locale.getRegion(); - len = regn.length(); + len = region.length(); for (int i = 0; i < len; i++) { - h = 31*h + LocaleUtils.toLower(regn.charAt(i)); + h = 31*h + LocaleUtils.toLower(region.charAt(i)); } - String vart = locale.getVariant(); - len = vart.length(); + len = variant.length(); for (int i = 0; i < len; i++) { - h = 31*h + vart.charAt(i); - } - return h; - } - - private BaseLocale getBaseLocale() { - return (holder == null) ? holderRef.get() : holder; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; + h = 31*h + variant.charAt(i); } - if (obj instanceof Key && this.hash == ((Key)obj).hash) { - BaseLocale other = ((Key) obj).getBaseLocale(); - BaseLocale locale = this.getBaseLocale(); - if (other != null && locale != null - && LocaleUtils.caseIgnoreMatch(other.getLanguage(), locale.getLanguage()) - && LocaleUtils.caseIgnoreMatch(other.getScript(), locale.getScript()) - && LocaleUtils.caseIgnoreMatch(other.getRegion(), locale.getRegion()) - // variant is case sensitive in JDK! - && other.getVariant().equals(locale.getVariant())) { - return true; - } - } - return false; - } - - public static Key normalize(Key key) { - if (key.normalized) { - return key; + if (h != 0) { + hash = h; } - - // Only normalized keys may be softly referencing the data holder - assert (key.holder != null && key.holderRef == null); - BaseLocale locale = key.holder; - return new Key(locale.getLanguage(), locale.getScript(), - locale.getRegion(), locale.getVariant(), true); - } - } - - private static class Cache extends LocaleObjectCache { - - private static final Cache CACHE = new Cache(); - - public Cache() { - } - - @Override - protected Key normalizeKey(Key key) { - return Key.normalize(key); - } - - @Override - protected BaseLocale createObject(Key key) { - return Key.normalize(key).getBaseLocale(); } + return h; } } diff --git a/src/java.base/share/classes/sun/util/locale/LocaleObjectCache.java b/src/java.base/share/classes/sun/util/locale/LocaleObjectCache.java deleted file mode 100644 index 50a1c2406ae..00000000000 --- a/src/java.base/share/classes/sun/util/locale/LocaleObjectCache.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2010, 2018, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 2009-2010, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ -package sun.util.locale; - -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -public abstract class LocaleObjectCache { - private final ConcurrentMap> map; - private final ReferenceQueue queue = new ReferenceQueue<>(); - - public LocaleObjectCache() { - this(16, 0.75f, 16); - } - - public LocaleObjectCache(int initialCapacity, float loadFactor, int concurrencyLevel) { - map = new ConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel); - } - - public V get(K key) { - V value = null; - - cleanStaleEntries(); - CacheEntry entry = map.get(key); - if (entry != null) { - value = entry.get(); - } - if (value == null) { - key = normalizeKey(key); - V newVal = createObject(key); - if (key == null || newVal == null) { - // subclass must return non-null key/value object - return null; - } - - CacheEntry newEntry = new CacheEntry<>(key, newVal, queue); - entry = map.putIfAbsent(key, newEntry); - if (entry == null) { - value = newVal; - } else { - value = entry.get(); - if (value == null) { - map.put(key, newEntry); - value = newVal; - } - } - } - return value; - } - - protected V put(K key, V value) { - CacheEntry entry = new CacheEntry<>(key, value, queue); - CacheEntry oldEntry = map.put(key, entry); - return (oldEntry == null) ? null : oldEntry.get(); - } - - @SuppressWarnings("unchecked") - private void cleanStaleEntries() { - CacheEntry entry; - while ((entry = (CacheEntry)queue.poll()) != null) { - map.remove(entry.getKey(), entry); - } - } - - protected abstract V createObject(K key); - - protected K normalizeKey(K key) { - return key; - } - - private static class CacheEntry extends SoftReference { - private K key; - - CacheEntry(K key, V value, ReferenceQueue queue) { - super(value, queue); - this.key = key; - } - - K getKey() { - return key; - } - } -} From ea36830ff9a8e9dc31b68f2013f08c0ffb033f53 Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Tue, 21 Jan 2025 15:43:07 +0100 Subject: [PATCH 2/5] Backport 8328261: public lookup fails with IllegalAccessException when ... --- .../share/classes/sun/invoke/util/VerifyAccess.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java b/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java index da896b93e1c..28373ad6bda 100644 --- a/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java +++ b/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java @@ -201,9 +201,10 @@ public static boolean isClassAccessible(Class refc, Module lookupModule = lookupClass.getModule(); Module refModule = refc.getModule(); - // early VM startup case, java.base not defined - if (lookupModule == null) { - assert refModule == null; + // early VM startup case, java.base not defined or + // module system is not fully initialized and exports are not set up + if (lookupModule == null || !jdk.internal.misc.VM.isModuleSystemInited()) { + assert lookupModule == refModule; return true; } @@ -230,11 +231,6 @@ public static boolean isClassAccessible(Class refc, if (isModuleAccessible(refc, lookupModule, prevLookupModule)) return true; - // not exported but allow access during VM initialization - // because java.base does not have its exports setup - if (!jdk.internal.misc.VM.isModuleSystemInited()) - return true; - // public class not accessible to lookupClass return false; } From 6cfd22bae096461a47b96756d2c685f3bb674529 Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Tue, 21 Jan 2025 16:05:44 +0100 Subject: [PATCH 3/5] Backport 8330802: Desugar switch in Locale::createLocale --- src/java.base/share/classes/java/util/Locale.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 66cdc112df0..669d83e69d6 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -927,11 +927,11 @@ static Locale getInstance(BaseLocale baseloc, LocaleExtensions extensions) { private static final ReferencedKeyMap LOCALE_CACHE = ReferencedKeyMap.create(true, ConcurrentHashMap::new); private static Locale createLocale(Object key) { - return switch (key) { - case BaseLocale base -> new Locale(base, null); - case LocaleKey lk -> new Locale(lk.base, lk.exts); - default -> throw new InternalError("should not happen"); - }; + if (key instanceof BaseLocale base) { + return new Locale(base, null); + } + LocaleKey lk = (LocaleKey)key; + return new Locale(lk.base, lk.exts); } private static final class LocaleKey { From ea07000495d11b04c0596424e94d93740d41de14 Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Tue, 21 Jan 2025 16:13:55 +0100 Subject: [PATCH 4/5] Backport 8331932: Startup regressions in 23-b13 --- .../share/classes/java/util/Locale.java | 26 ++++++++++++------- .../jdk/internal/util/ReferencedKeyMap.java | 15 +++++++++++ .../jdk/internal/util/ReferencedKeySet.java | 9 +++++++ .../classes/sun/util/locale/BaseLocale.java | 22 +++++++++++----- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 669d83e69d6..5f7d1d856c7 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -47,6 +47,7 @@ import java.io.Serializable; import java.text.MessageFormat; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.spi.LocaleNameProvider; import java.util.stream.Stream; @@ -912,29 +913,36 @@ static Locale getInstance(String language, String script, String country, return getInstance(baseloc, extensions); } + static Locale getInstance(BaseLocale baseloc, LocaleExtensions extensions) { if (extensions == null) { Locale locale = CONSTANT_LOCALES.get(baseloc); if (locale != null) { return locale; } - return LOCALE_CACHE.computeIfAbsent(baseloc, Locale::createLocale); + return LOCALE_CACHE.computeIfAbsent(baseloc, LOCALE_CREATOR); } else { LocaleKey key = new LocaleKey(baseloc, extensions); - return LOCALE_CACHE.computeIfAbsent(key, Locale::createLocale); + return LOCALE_CACHE.computeIfAbsent(key, LOCALE_CREATOR); } } - private static final ReferencedKeyMap LOCALE_CACHE = ReferencedKeyMap.create(true, ConcurrentHashMap::new); - private static Locale createLocale(Object key) { - if (key instanceof BaseLocale base) { - return new Locale(base, null); + private static final ReferencedKeyMap LOCALE_CACHE + = ReferencedKeyMap.create(true, ReferencedKeyMap.concurrentHashMapSupplier()); + + private static final Function LOCALE_CREATOR = new Function<>() { + @Override + public Locale apply(Object key) { + if (key instanceof BaseLocale base) { + return new Locale(base, null); + } + LocaleKey lk = (LocaleKey)key; + return new Locale(lk.base, lk.exts); } - LocaleKey lk = (LocaleKey)key; - return new Locale(lk.base, lk.exts); - } + }; private static final class LocaleKey { + private final BaseLocale base; private final LocaleExtensions exts; private final int hash; diff --git a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java index f18cb05a8da..810d43ae38a 100644 --- a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java +++ b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java @@ -99,6 +99,21 @@ public final class ReferencedKeyMap implements Map { */ private final ReferenceQueue stale; + /** + * @return a supplier to create a {@code ConcurrentHashMap} appropriate for use in the + * create methods. + * @param the type of keys maintained by the new map + * @param the type of mapped values + */ + public static Supplier, V>> concurrentHashMapSupplier() { + return new Supplier<>() { + @Override + public Map, V> get() { + return new ConcurrentHashMap<>(); + } + }; + } + /** * Private constructor. * diff --git a/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java b/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java index 21b940439e0..73ad6f32640 100644 --- a/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java +++ b/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java @@ -75,6 +75,15 @@ public final class ReferencedKeySet extends AbstractSet { */ final ReferencedKeyMap> map; + /** + * @return a supplier to create a {@code ConcurrentHashMap} appropriate for use in the + * create methods. + * @param the type of elements maintained by this set + */ + public static Supplier, ReferenceKey>> concurrentHashMapSupplier() { + return ReferencedKeyMap.concurrentHashMapSupplier(); + } + /** * Private constructor. * diff --git a/src/java.base/share/classes/sun/util/locale/BaseLocale.java b/src/java.base/share/classes/sun/util/locale/BaseLocale.java index 7e0fc9a2d34..ec2a6a49183 100644 --- a/src/java.base/share/classes/sun/util/locale/BaseLocale.java +++ b/src/java.base/share/classes/sun/util/locale/BaseLocale.java @@ -38,7 +38,7 @@ import jdk.internal.vm.annotation.Stable; import java.util.StringJoiner; -import java.util.concurrent.ConcurrentHashMap; +import java.util.function.UnaryOperator; public final class BaseLocale { @@ -93,7 +93,7 @@ public final class BaseLocale { // Interned BaseLocale cache private static final ReferencedKeySet CACHE = - ReferencedKeySet.create(true, ConcurrentHashMap::new); + ReferencedKeySet.create(true, ReferencedKeySet.concurrentHashMapSupplier()); public static final String SEP = "_"; @@ -164,13 +164,21 @@ public static BaseLocale getInstance(String language, String script, // "interned" instance can subsequently be used by the Locale // instance which guarantees the locale components are properly cased/interned. return CACHE.intern(new BaseLocale(language, script, region, variant), - (b) -> new BaseLocale( - LocaleUtils.toLowerString(b.language).intern(), - LocaleUtils.toTitleString(b.script).intern(), - LocaleUtils.toUpperString(b.region).intern(), - b.variant.intern())); + // Avoid lambdas since this may be on the bootstrap path in many locales + INTERNER); } + public static final UnaryOperator INTERNER = new UnaryOperator<>() { + @Override + public BaseLocale apply(BaseLocale b) { + return new BaseLocale( + LocaleUtils.toLowerString(b.language).intern(), + LocaleUtils.toTitleString(b.script).intern(), + LocaleUtils.toUpperString(b.region).intern(), + b.variant.intern()); + } + }; + public static String convertOldISOCodes(String language) { return switch (language) { case "he", "iw" -> OLD_ISO_CODES ? "iw" : "he"; From 525e8b50cf74e147fbb94fd8e93133bbb9c476b9 Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Tue, 21 Jan 2025 16:14:13 +0100 Subject: [PATCH 5/5] Backport 8338897: Small startup regression remains after JDK-8309622 and JDK-8331932 --- .../share/classes/java/util/Locale.java | 18 ++++++++---- .../classes/sun/util/locale/BaseLocale.java | 28 ++++++------------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 5f7d1d856c7..c0fd0361c03 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -920,17 +920,23 @@ static Locale getInstance(BaseLocale baseloc, LocaleExtensions extensions) { if (locale != null) { return locale; } - return LOCALE_CACHE.computeIfAbsent(baseloc, LOCALE_CREATOR); + return LocaleCache.cache(baseloc); } else { LocaleKey key = new LocaleKey(baseloc, extensions); - return LOCALE_CACHE.computeIfAbsent(key, LOCALE_CREATOR); + return LocaleCache.cache(key); } } - private static final ReferencedKeyMap LOCALE_CACHE - = ReferencedKeyMap.create(true, ReferencedKeyMap.concurrentHashMapSupplier()); + private static final class LocaleCache implements Function { + private static final ReferencedKeyMap LOCALE_CACHE + = ReferencedKeyMap.create(true, ReferencedKeyMap.concurrentHashMapSupplier()); + + private static final Function LOCALE_CREATOR = new LocaleCache(); + + public static Locale cache(Object key) { + return LOCALE_CACHE.computeIfAbsent(key, LOCALE_CREATOR); + } - private static final Function LOCALE_CREATOR = new Function<>() { @Override public Locale apply(Object key) { if (key instanceof BaseLocale base) { @@ -939,7 +945,7 @@ public Locale apply(Object key) { LocaleKey lk = (LocaleKey)key; return new Locale(lk.base, lk.exts); } - }; + } private static final class LocaleKey { diff --git a/src/java.base/share/classes/sun/util/locale/BaseLocale.java b/src/java.base/share/classes/sun/util/locale/BaseLocale.java index ec2a6a49183..a744b6ce39f 100644 --- a/src/java.base/share/classes/sun/util/locale/BaseLocale.java +++ b/src/java.base/share/classes/sun/util/locale/BaseLocale.java @@ -38,7 +38,6 @@ import jdk.internal.vm.annotation.Stable; import java.util.StringJoiner; -import java.util.function.UnaryOperator; public final class BaseLocale { @@ -91,10 +90,6 @@ public final class BaseLocale { } } - // Interned BaseLocale cache - private static final ReferencedKeySet CACHE = - ReferencedKeySet.create(true, ReferencedKeySet.concurrentHashMapSupplier()); - public static final String SEP = "_"; private final String language; @@ -163,21 +158,16 @@ public static BaseLocale getInstance(String language, String script, // Obtain the "interned" BaseLocale from the cache. The returned // "interned" instance can subsequently be used by the Locale // instance which guarantees the locale components are properly cased/interned. - return CACHE.intern(new BaseLocale(language, script, region, variant), - // Avoid lambdas since this may be on the bootstrap path in many locales - INTERNER); - } - - public static final UnaryOperator INTERNER = new UnaryOperator<>() { - @Override - public BaseLocale apply(BaseLocale b) { - return new BaseLocale( - LocaleUtils.toLowerString(b.language).intern(), - LocaleUtils.toTitleString(b.script).intern(), - LocaleUtils.toUpperString(b.region).intern(), - b.variant.intern()); + class InterningCache { // TODO: StableValue + private static final ReferencedKeySet CACHE = + ReferencedKeySet.create(true, ReferencedKeySet.concurrentHashMapSupplier()); } - }; + return InterningCache.CACHE.intern(new BaseLocale( + language.intern(), // guaranteed to be lower-case + LocaleUtils.toTitleString(script).intern(), + region.intern(), // guaranteed to be upper-case + variant.intern())); + } public static String convertOldISOCodes(String language) { return switch (language) {