8076287: Performance degradation observed with TimeZone Benchmark

Reviewed-by: okutsu
This commit is contained in:
Naoto Sato 2015-04-16 08:25:19 -07:00
parent 41b53b0d3c
commit 5b2c289414
6 changed files with 92 additions and 176 deletions

View File

@ -47,6 +47,7 @@ import java.util.Calendar;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -250,17 +251,17 @@ public class LocaleResources {
return (String) localeName;
}
String[] getTimeZoneNames(String key, int size) {
String[] getTimeZoneNames(String key) {
String[] names = null;
String cacheKey = TIME_ZONE_NAMES + size + '.' + key;
String cacheKey = TIME_ZONE_NAMES + '.' + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data == null || ((names = (String[]) data.get()) == null)) {
if (Objects.isNull(data) || Objects.isNull((names = (String[]) data.get()))) {
TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
if (tznb.containsKey(key)) {
names = tznb.getStringArray(key, size);
names = tznb.getStringArray(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) names, referenceQueue));
}

View File

@ -26,6 +26,7 @@
package sun.util.locale.provider;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.spi.TimeZoneNameProvider;
@ -95,8 +96,9 @@ public class TimeZoneNameProviderImpl extends TimeZoneNameProvider {
*/
@Override
public String getDisplayName(String id, boolean daylight, int style, Locale locale) {
String[] names = getDisplayNameArray(id, 5, locale);
if (names != null) {
String[] names = getDisplayNameArray(id, locale);
if (Objects.nonNull(names)) {
assert names.length >= 7;
int index = daylight ? 3 : 1;
if (style == TimeZone.SHORT) {
index++;
@ -108,18 +110,18 @@ public class TimeZoneNameProviderImpl extends TimeZoneNameProvider {
@Override
public String getGenericDisplayName(String id, int style, Locale locale) {
String[] names = getDisplayNameArray(id, 7, locale);
if (names != null && names.length >= 7) {
String[] names = getDisplayNameArray(id, locale);
if (Objects.nonNull(names)) {
assert names.length >= 7;
return names[(style == TimeZone.LONG) ? 5 : 6];
}
return null;
}
private String[] getDisplayNameArray(String id, int n, Locale locale) {
if (id == null || locale == null) {
throw new NullPointerException();
}
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getTimeZoneNames(id, n);
private String[] getDisplayNameArray(String id, Locale locale) {
Objects.requireNonNull(id);
Objects.requireNonNull(locale);
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getTimeZoneNames(id);
}
/**

View File

@ -30,6 +30,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.spi.TimeZoneNameProvider;
@ -100,9 +101,9 @@ public final class TimeZoneNameUtility {
* Retrieve display names for a time zone ID.
*/
public static String[] retrieveDisplayNames(String id, Locale locale) {
if (id == null || locale == null) {
throw new NullPointerException();
}
Objects.requireNonNull(id);
Objects.requireNonNull(locale);
return retrieveDisplayNamesImpl(id, locale);
}
@ -115,9 +116,12 @@ public final class TimeZoneNameUtility {
* @return the requested generic time zone display name, or null if not found.
*/
public static String retrieveGenericDisplayName(String id, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, "generic", style, id);
String[] names = retrieveDisplayNamesImpl(id, locale);
if (Objects.nonNull(names)) {
return names[6 - style];
} else {
return null;
}
}
/**
@ -130,140 +134,53 @@ public final class TimeZoneNameUtility {
* @return the requested time zone name, or null if not found.
*/
public static String retrieveDisplayName(String id, boolean daylight, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, daylight ? "dst" : "std", style, id);
String[] names = retrieveDisplayNamesImpl(id, locale);
if (Objects.nonNull(names)) {
return names[(daylight ? 4 : 2) - style];
} else {
return null;
}
}
private static String[] retrieveDisplayNamesImpl(String id, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
String[] names;
Map<Locale, String[]> perLocale = null;
SoftReference<Map<Locale, String[]>> ref = cachedDisplayNames.get(id);
if (ref != null) {
Map<Locale, String[]> perLocale = ref.get();
if (perLocale != null) {
String[] names = perLocale.get(locale);
if (names != null) {
if (Objects.nonNull(ref)) {
perLocale = ref.get();
if (Objects.nonNull(perLocale)) {
names = perLocale.get(locale);
if (Objects.nonNull(names)) {
return names;
}
names = pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id);
if (names != null) {
perLocale.put(locale, names);
}
return names;
}
}
String[] names = pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id);
if (names != null) {
Map<Locale, String[]> perLocale = new ConcurrentHashMap<>();
perLocale.put(locale, names);
ref = new SoftReference<>(perLocale);
cachedDisplayNames.put(id, ref);
// build names array
names = new String[7];
names[0] = id;
for (int i = 1; i <= 6; i ++) {
names[i] = pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale,
i<5 ? (i<3 ? "std" : "dst") : "generic", i%2, id);
}
if (Objects.isNull(perLocale)) {
perLocale = new ConcurrentHashMap<>();
}
perLocale.put(locale, names);
ref = new SoftReference<>(perLocale);
cachedDisplayNames.put(id, ref);
return names;
}
/**
* Obtains a localized time zone strings from a TimeZoneNameProvider
* implementation.
*/
private static class TimeZoneNameArrayGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider,
String[]>{
private static final TimeZoneNameArrayGetter INSTANCE =
new TimeZoneNameArrayGetter();
@Override
public String[] getObject(TimeZoneNameProvider timeZoneNameProvider,
Locale locale,
String requestID,
Object... params) {
assert params.length == 0;
// First, try to get names with the request ID
String[] names = buildZoneStrings(timeZoneNameProvider, locale, requestID);
if (names == null) {
Map<String, String> aliases = ZoneInfo.getAliasTable();
if (aliases != null) {
// Check whether this id is an alias, if so,
// look for the standard id.
String canonicalID = aliases.get(requestID);
if (canonicalID != null) {
names = buildZoneStrings(timeZoneNameProvider, locale, canonicalID);
}
if (names == null) {
// There may be a case that a standard id has become an
// alias. so, check the aliases backward.
names = examineAliases(timeZoneNameProvider, locale,
canonicalID == null ? requestID : canonicalID, aliases);
}
}
}
if (names != null) {
names[0] = requestID;
}
return names;
}
private static String[] examineAliases(TimeZoneNameProvider tznp, Locale locale,
String id,
Map<String, String> aliases) {
if (aliases.containsValue(id)) {
for (Map.Entry<String, String> entry : aliases.entrySet()) {
if (entry.getValue().equals(id)) {
String alias = entry.getKey();
String[] names = buildZoneStrings(tznp, locale, alias);
if (names != null) {
return names;
}
names = examineAliases(tznp, locale, alias, aliases);
if (names != null) {
return names;
}
}
}
}
return null;
}
private static String[] buildZoneStrings(TimeZoneNameProvider tznp,
Locale locale, String id) {
String[] names = new String[5];
for (int i = 1; i <= 4; i ++) {
names[i] = tznp.getDisplayName(id, i>=3, i%2, locale);
if (names[i] == null) {
switch (i) {
case 1:
// this id seems not localized by this provider
return null;
case 2:
case 4:
// If the display name for SHORT is not supplied,
// copy the LONG name.
names[i] = names[i-1];
break;
case 3:
// If the display name for DST is not supplied,
// copy the "standard" name.
names[3] = names[1];
break;
}
}
}
return names;
}
}
private static class TimeZoneNameGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider,
String> {
@ -299,18 +216,16 @@ public final class TimeZoneNameUtility {
private static String examineAliases(TimeZoneNameProvider tznp, Locale locale,
String requestID, String tzid, int style,
Map<String, String> aliases) {
if (aliases.containsValue(tzid)) {
for (Map.Entry<String, String> entry : aliases.entrySet()) {
if (entry.getValue().equals(tzid)) {
String alias = entry.getKey();
String name = getName(tznp, locale, requestID, style, alias);
if (name != null) {
return name;
}
name = examineAliases(tznp, locale, requestID, alias, style, aliases);
if (name != null) {
return name;
}
for (Map.Entry<String, String> entry : aliases.entrySet()) {
if (entry.getValue().equals(tzid)) {
String alias = entry.getKey();
String name = getName(tznp, locale, requestID, style, alias);
if (name != null) {
return name;
}
name = examineAliases(tznp, locale, requestID, alias, style, aliases);
if (name != null) {
return name;
}
}
}

View File

@ -44,6 +44,7 @@ import java.util.Map;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Set;
/**
@ -60,26 +61,6 @@ import java.util.Set;
*/
public abstract class TimeZoneNamesBundle extends OpenListResourceBundle {
/**
* Returns a String array containing time zone names. The String array has
* at most size elements.
*
* @param key the time zone ID for which names are obtained
* @param size the requested size of array for names
* @return a String array containing names
*/
public String[] getStringArray(String key, int size) {
String[] names = handleGetObject(key, size);
if ((names == null || names.length != size) && parent != null) {
names = ((TimeZoneNamesBundle)parent).getStringArray(key, size);
}
if (names == null) {
throw new MissingResourceException("no time zone names", getClass().getName(), key);
}
return names;
}
/**
* Maps time zone IDs to locale-specific names.
* The value returned is an array of five strings:
@ -89,6 +70,8 @@ public abstract class TimeZoneNamesBundle extends OpenListResourceBundle {
* <li>The short name of the time zone in standard time (localized).
* <li>The long name of the time zone in daylight savings time (localized).
* <li>The short name of the time zone in daylight savings time (localized).
* <li>The long name of the time zone in generic form (localized).
* <li>The short name of the time zone in generic form (localized).
* </ul>
* The localized names come from the subclasses's
* <code>getContents</code> implementations, while the time zone
@ -96,16 +79,12 @@ public abstract class TimeZoneNamesBundle extends OpenListResourceBundle {
*/
@Override
public Object handleGetObject(String key) {
return handleGetObject(key, 5);
}
private String[] handleGetObject(String key, int n) {
String[] contents = (String[]) super.handleGetObject(key);
if (contents == null) {
if (Objects.isNull(contents)) {
return null;
}
int clen = Math.min(n - 1, contents.length);
String[] tmpobj = new String[clen+1];
int clen = contents.length;
String[] tmpobj = new String[7];
tmpobj[0] = key;
System.arraycopy(contents, 0, tmpobj, 1, clen);
return tmpobj;

View File

@ -47,7 +47,8 @@ public final class TimeZoneNames_en_IE extends TimeZoneNamesBundle {
protected final Object[][] getContents() {
return new Object[][] {
{"Europe/London", new String[] {"Greenwich Mean Time", "GMT",
"Irish Summer Time", "IST" /*Dublin*/}},
"Irish Summer Time", "IST", /*Dublin*/
"Irish Time", "IT" /*Dublin*/}},
};
}
}

View File

@ -25,6 +25,7 @@
*/
import java.text.*;
import java.time.format.TextStyle;
import java.util.*;
import sun.util.locale.provider.*;
import sun.util.resources.*;
@ -42,6 +43,7 @@ public class TimeZoneNameProviderTest extends ProviderTest {
test2();
test3();
aliasTest();
genericFallbackTest();
}
void test1() {
@ -169,9 +171,9 @@ public class TimeZoneNameProviderTest extends ProviderTest {
for (int style : new int[] { TimeZone.LONG, TimeZone.SHORT }) {
String osakaStd = tz.getDisplayName(false, style, OSAKA);
if (osakaStd != null) {
// No API for getting generic time zone names
String generic = TimeZoneNameUtility.retrieveGenericDisplayName(tzname,
style, GENERIC);
String generic = tz.toZoneId().getDisplayName(
style == TimeZone.LONG ? TextStyle.FULL : TextStyle.SHORT,
GENERIC);
String expected = "Generic " + osakaStd;
if (!expected.equals(generic)) {
throw new RuntimeException("Wrong generic name: got=\"" + generic
@ -230,4 +232,20 @@ public class TimeZoneNameProviderTest extends ProviderTest {
throw new RuntimeException("Provider's localized name is not available for an alias ID: "+JAPAN+". result: "+japan+" expected: "+JST_IN_OSAKA);
}
}
/*
* Tests whether generic names can be retrieved through fallback.
* The test assumes the provider impl for OSAKA locale does NOT
* provide generic names.
*/
final String PT = "PT"; // SHORT generic name for "America/Los_Angeles"
void genericFallbackTest() {
String generic =
TimeZone.getTimeZone(LATIME)
.toZoneId()
.getDisplayName(TextStyle.SHORT, OSAKA);
if (!PT.equals(generic)) {
throw new RuntimeException("Generic name fallback failed. got: "+generic);
}
}
}