diff --git a/icu4j/main/core/src/main/java/com/ibm/icu/impl/ICUResourceBundleReader.java b/icu4j/main/core/src/main/java/com/ibm/icu/impl/ICUResourceBundleReader.java index 6244c1a6356d..990efb05e95b 100644 --- a/icu4j/main/core/src/main/java/com/ibm/icu/impl/ICUResourceBundleReader.java +++ b/icu4j/main/core/src/main/java/com/ibm/icu/impl/ICUResourceBundleReader.java @@ -21,6 +21,8 @@ import java.nio.CharBuffer; import java.nio.IntBuffer; import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** @@ -1232,10 +1234,80 @@ Object putIfAbsent(int res, Object item, int size) { }); return result[0]; } + + synchronized void deduplicateTableArrays() { + Map charMap = new HashMap<>(); + Map intMap = new HashMap<>(); + Map stringMap = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + entry.setValue(deduplicateItem(entry.getValue(), charMap, intMap, stringMap)); + } + } + + @SuppressWarnings("unchecked") + private static Object deduplicateItem( + Object value, + Map charMap, + Map intMap, + Map stringMap) { + Object item = value; + if (item instanceof SoftReference) { + item = ((SoftReference) item).get(); + } + if (item instanceof Table) { + Table table = (Table) item; + if (table.keyOffsets != null) { + CharBuffer cb = CharBuffer.wrap(table.keyOffsets); + char[] existing = charMap.putIfAbsent(cb, table.keyOffsets); + if (existing != null) { + table.keyOffsets = existing; + } + } + if (table.key32Offsets != null) { + IntBuffer ib = IntBuffer.wrap(table.key32Offsets); + int[] existing = intMap.putIfAbsent(ib, table.key32Offsets); + if (existing != null) { + table.key32Offsets = existing; + } + } + } else if (item instanceof String) { + String s = (String) item; + String existing = stringMap.putIfAbsent(s, s); + if (existing != null) { + item = existing; + } + if (value instanceof SoftReference) { + return new SoftReference<>(item); + } else { + return item; + } + } + return value; + } } private static final String ICU_RESOURCE_SUFFIX = ".res"; + /** Deduplicates the char[] and int[] arrays of Table objects in the cache. */ + public static void deduplicateTableArrays() { + for (Object mapValue : CACHE.getMap().values()) { + ICUResourceBundleReader reader = null; + if (mapValue instanceof CacheValue) { + @SuppressWarnings("unchecked") + CacheValue cv = + (CacheValue) mapValue; + if (!cv.isNull()) { + reader = cv.get(); + } + } else if (mapValue instanceof ICUResourceBundleReader) { + reader = (ICUResourceBundleReader) mapValue; + } + if (reader != null && reader.resourceCache != null) { + reader.resourceCache.deduplicateTableArrays(); + } + } + } + /** Gets the full name of the resource with suffix. */ public static String getFullName(String baseName, String localeName) { if (baseName == null || baseName.length() == 0) { diff --git a/icu4j/main/core/src/main/java/com/ibm/icu/impl/SoftCache.java b/icu4j/main/core/src/main/java/com/ibm/icu/impl/SoftCache.java index f734142087b5..3e28acab10f7 100644 --- a/icu4j/main/core/src/main/java/com/ibm/icu/impl/SoftCache.java +++ b/icu4j/main/core/src/main/java/com/ibm/icu/impl/SoftCache.java @@ -32,6 +32,13 @@ public abstract class SoftCache extends CacheBase { private ConcurrentHashMap map = new ConcurrentHashMap(); + /** + * @return the cache contents. + */ + protected ConcurrentHashMap getMap() { + return map; + } + @SuppressWarnings("unchecked") @Override public final V getInstance(K key, D data) {