Optimize server-side LinkedHashMap_CustomFieldSerializer.
1) Don't keep trying reflection if it's not working.
2) Cache the reflective Field needed for accessOrder.
3) Reuse KEY objects instead of making new ones each time.
http://gwt-code-reviews.appspot.com/829802/show
Review by: rice
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8777 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java b/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java
index 222692e..62b6073 100644
--- a/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java
@@ -21,14 +21,32 @@
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Custom field serializer for {@link java.util.LinkedHashMap} for the server
* (uses reflection).
*/
+@SuppressWarnings("unchecked")
public final class LinkedHashMap_CustomFieldSerializer {
- @SuppressWarnings("unchecked") // raw LinkedHashMap
+ /**
+ * We use an atomic reference to avoid having to synchronize. This is safe
+ * because it's only used as a cache; it's okay to read a stale value.
+ */
+ private static AtomicReference<Field> accessOrderField = new AtomicReference<Field>(
+ null);
+
+ private static Object KEY1 = new Object();
+ private static Object KEY2 = new Object();
+
+ /**
+ * We use an atomic reference to avoid having to synchronize. This is safe
+ * because it's only used as a cache; it's okay to read a stale value.
+ */
+ private static AtomicBoolean reflectionHasFailed = new AtomicBoolean(false);
+
public static void deserialize(SerializationStreamReader streamReader,
LinkedHashMap instance) throws SerializationException {
Map_CustomFieldSerializerBase.deserialize(streamReader, instance);
@@ -42,12 +60,11 @@
* @param instance the instance to check
* @return the value of instance.accessOrder
*/
- @SuppressWarnings("unchecked") // raw LinkedHashMap
- public static boolean getAccessOrderNoReflection(LinkedHashMap instance) {
+ public static boolean getAccessOrderNoReflection(LinkedHashMap instance) {
/*
- * Clone the instance so our modifications won't affect the original.
- * In particular, if the original overrides removeEldestEntry, adding
- * elements to the map could cause existing elements to be removed.
+ * Clone the instance so our modifications won't affect the original. In
+ * particular, if the original overrides removeEldestEntry, adding elements
+ * to the map could cause existing elements to be removed.
*/
instance = (LinkedHashMap) instance.clone();
instance.clear();
@@ -63,56 +80,49 @@
* (accessOrder = true), we will encounter key2 first, since key1 has been
* accessed more recently.
*/
- Object key1 = new Object();
- Object key2 = new Object();
- instance.put(key1, key1); // INSERT key1
- instance.put(key2, key2); // INSERT key2
- instance.get(key1); // ACCESS key1
- boolean accessOrder = false;
- for (Object key : instance.keySet()) {
- if (key == key1) {
- break;
- }
- if (key == key2) {
- accessOrder = true;
- break;
- }
- }
-
- return accessOrder;
+ instance.put(KEY1, KEY1); // INSERT key1
+ instance.put(KEY2, KEY2); // INSERT key2
+ instance.get(KEY1); // ACCESS key1
+ return instance.keySet().iterator().next() == KEY2;
}
- @SuppressWarnings("unchecked") // raw LinkedHashMap
public static LinkedHashMap instantiate(SerializationStreamReader streamReader)
throws SerializationException {
boolean accessOrder = streamReader.readBoolean();
return new LinkedHashMap(16, .75f, accessOrder);
}
- @SuppressWarnings("unchecked") // raw LinkedHashMap
public static void serialize(SerializationStreamWriter streamWriter,
LinkedHashMap instance) throws SerializationException {
streamWriter.writeBoolean(getAccessOrder(instance));
Map_CustomFieldSerializerBase.serialize(streamWriter, instance);
}
- @SuppressWarnings("unchecked") // raw LinkedHashMap
private static boolean getAccessOrder(LinkedHashMap instance) {
- Field accessOrderField;
- try {
- accessOrderField = LinkedHashMap.class.getDeclaredField("accessOrder");
- accessOrderField.setAccessible(true);
- return ((Boolean) accessOrderField.get(instance)).booleanValue();
- } catch (SecurityException e) {
- // fall through
- } catch (NoSuchFieldException e) {
- // fall through
- } catch (IllegalArgumentException e) {
- // fall through
- } catch (IllegalAccessException e) {
- // fall through
+ if (!reflectionHasFailed.get()) {
+ try {
+ Field f = accessOrderField.get();
+ if (f == null || !f.isAccessible()) {
+ f = LinkedHashMap.class.getDeclaredField("accessOrder");
+ synchronized (f) {
+ // Ensure all threads can see the accessibility.
+ f.setAccessible(true);
+ }
+ accessOrderField.set(f);
+ }
+ return ((Boolean) f.get(instance)).booleanValue();
+ } catch (SecurityException e) {
+ // fall through
+ } catch (NoSuchFieldException e) {
+ // fall through
+ } catch (IllegalArgumentException e) {
+ // fall through
+ } catch (IllegalAccessException e) {
+ // fall through
+ }
+ reflectionHasFailed.set(true);
}
-
+
// Use a (possibly slower) technique that does not require reflection.
return getAccessOrderNoReflection(instance);
}