You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/07/27 23:46:47 UTC

[groovy] branch master updated: GROOVY-9631: Replace legacy data structure with Java collection(master only)(closes #1306)

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new acd53b3  GROOVY-9631: Replace legacy data structure with Java collection(master only)(closes #1306)
acd53b3 is described below

commit acd53b38e72a5e6f64a40f7cc0d06df3472774a1
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Jul 12 23:06:46 2020 +0800

    GROOVY-9631: Replace legacy data structure with Java collection(master only)(closes #1306)
---
 src/main/java/groovy/lang/MetaClassImpl.java       | 132 +++++++++---------
 .../java/org/codehaus/groovy/ast/ClassHelper.java  |   4 +-
 .../org/codehaus/groovy/reflection/ClassInfo.java  |   6 +-
 .../reflection/GroovyClassValuePreJava7.java       |   1 +
 .../groovy/reflection/MixinInMetaClass.java        |  19 ++-
 .../groovy/reflection/ReflectionCache.java         |   2 +
 .../groovy/runtime/memoize/MemoizeCache.java       |   2 +-
 .../metaclass/ThreadManagedMetaBeanProperty.java   |  16 +--
 .../groovy/util/AbstractConcurrentMap.java         |   1 +
 .../groovy/util/AbstractConcurrentMapBase.java     |   1 +
 .../codehaus/groovy/util/ComplexKeyHashMap.java    |   1 +
 .../codehaus/groovy/util/ManagedConcurrentMap.java |   1 +
 .../groovy/util/ManagedIdentityConcurrentMap.java  | 149 +++++++++++++++++++++
 .../org/codehaus/groovy/util/SingleKeyHashMap.java |   1 +
 .../org/codehaus/groovy/util/TripleKeyHashMap.java |   1 +
 .../util/ManagedIdentityConcurrentMapTest.groovy   |  60 +++++++++
 .../groovy-json/src/spec/test/json/JsonTest.groovy |   4 +-
 .../test/groovy/groovy/json/JsonOutputTest.groovy  |  28 ++--
 18 files changed, 324 insertions(+), 105 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 59d45e6..3aa3a3d 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -101,9 +101,8 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -151,14 +150,14 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
     protected final boolean isMap;
     protected final MetaMethodIndex metaMethodIndex;
 
-    private final Index classPropertyIndex = new MethodIndex();
-    private final SingleKeyHashMap staticPropertyIndex = new SingleKeyHashMap();
-    private final Map<String, MetaMethod> listeners = new HashMap<>();
+    private final Map<CachedClass, LinkedHashMap<String, MetaProperty>> classPropertyIndex = new LinkedHashMap<>();
+    private final Map<String, MetaProperty> staticPropertyIndex = new LinkedHashMap<>();
+    private final Map<String, MetaMethod> listeners = new LinkedHashMap<>();
     private final List<MetaMethod> allMethods = new ArrayList<>();
     // we only need one of these that can be reused over and over.
     private final MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
-    private final Index classPropertyIndexForSuper = new MethodIndex();
-    private final Set<MetaMethod> newGroovyMethodsSet = new HashSet<>();
+    private final Map<CachedClass, LinkedHashMap<String, MetaProperty>> classPropertyIndexForSuper = new LinkedHashMap<>();
+    private final Set<MetaMethod> newGroovyMethodsSet = new LinkedHashSet<>();
     private final MetaMethod[] myNewMetaMethods;
     private final MetaMethod[] additionalMetaMethods;
 
@@ -290,13 +289,13 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
     public MetaProperty getMetaProperty(final String name) {
         MetaProperty metaProperty = null;
 
-        SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
-        metaProperty = (MetaProperty) propertyMap.get(name);
+        LinkedHashMap<String, MetaProperty> propertyMap = classPropertyIndex.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
+        metaProperty = propertyMap.get(name);
         if (metaProperty == null) {
-            metaProperty = (MetaProperty) staticPropertyIndex.get(name);
+            metaProperty = staticPropertyIndex.get(name);
             if (metaProperty == null) {
-                propertyMap = classPropertyIndexForSuper.getNotNull(theCachedClass);
-                metaProperty = (MetaProperty) propertyMap.get(name);
+                propertyMap = classPropertyIndexForSuper.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
+                metaProperty = propertyMap.get(name);
                 if (metaProperty == null) {
                     MetaBeanProperty property = findPropertyInClassHierarchy(name, theCachedClass);
                     if (property != null) {
@@ -1632,7 +1631,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         };
         l.sort(comp);
         int found = -1;
-        for (int i = 0; i < l.size(); i++) {
+        for (int i = 0, n = l.size(); i < n; i++) {
             if (l.get(i) != constructor) continue;
             found = i;
             break;
@@ -2215,16 +2214,16 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
      */
     public List<MetaProperty> getProperties() {
         checkInitalised();
-        SingleKeyHashMap propertyMap = classPropertyIndex.getNullable(theCachedClass);
+        LinkedHashMap<String, MetaProperty> propertyMap = classPropertyIndex.get(theCachedClass);
         if (propertyMap == null) {
             // GROOVY-6903: May happen in some special environment, like under Android, due
             // to classloading issues
-            propertyMap = new SingleKeyHashMap();
+            propertyMap = new LinkedHashMap<>();
         }
         // simply return the values of the metaproperty map as a List
-        List ret = new ArrayList(propertyMap.size());
-        for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext(); ) {
-            MetaProperty element = (MetaProperty) ((SingleKeyHashMap.Entry) iter.next()).value;
+        List<MetaProperty> ret = new ArrayList<>(propertyMap.size());
+        for (Map.Entry<String, MetaProperty> stringMetaPropertyEntry : propertyMap.entrySet()) {
+            MetaProperty element = stringMetaPropertyEntry.getValue();
             if (element instanceof CachedField) continue;
             // filter out DGM beans
             if (element instanceof MetaBeanProperty) {
@@ -2372,23 +2371,21 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
      *
      * @param propertyDescriptors the property descriptors
      */
-    @SuppressWarnings("unchecked")
     private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
         if (theCachedClass.isInterface) {
             LinkedList<CachedClass> superClasses = new LinkedList<>();
             superClasses.add(ReflectionCache.OBJECT_CLASS);
-            Set interfaces = theCachedClass.getInterfaces();
+            LinkedList<CachedClass> superInterfaces = new LinkedList<>(theCachedClass.getInterfaces());
 
-            LinkedList<CachedClass> superInterfaces = new LinkedList<CachedClass>(interfaces);
             // sort interfaces so that we may ensure a deterministic behaviour in case of
             // ambiguous fields (class implementing two interfaces using the same field)
             if (superInterfaces.size() > 1) {
                 superInterfaces.sort(CACHED_CLASS_NAME_COMPARATOR);
             }
 
-            SingleKeyHashMap iPropertyIndex = classPropertyIndex.getNotNull(theCachedClass);
+            Map<String, MetaProperty> iPropertyIndex = classPropertyIndex.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
             for (CachedClass iclass : superInterfaces) {
-                SingleKeyHashMap sPropertyIndex = classPropertyIndex.getNotNull(iclass);
+                Map<String, MetaProperty> sPropertyIndex = classPropertyIndex.computeIfAbsent(iclass, k -> new LinkedHashMap<>());
                 copyNonPrivateFields(sPropertyIndex, iPropertyIndex, null);
                 addFields(iclass, iPropertyIndex);
             }
@@ -2400,21 +2397,22 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
             makeStaticPropertyIndex();
         } else {
             LinkedList<CachedClass> superClasses = getSuperClasses();
-            LinkedList<CachedClass> interfaces = new LinkedList<>(theCachedClass.getInterfaces());
+            LinkedList<CachedClass> superInterfaces = new LinkedList<>(theCachedClass.getInterfaces());
+
             // sort interfaces so that we may ensure a deterministic behaviour in case of
             // ambiguous fields (class implementing two interfaces using the same field)
-            if (interfaces.size() > 1) {
-                interfaces.sort(CACHED_CLASS_NAME_COMPARATOR);
+            if (superInterfaces.size() > 1) {
+                superInterfaces.sort(CACHED_CLASS_NAME_COMPARATOR);
             }
 
             // if this an Array, then add the special read-only "length" property
             if (theCachedClass.isArray) {
-                SingleKeyHashMap map = new SingleKeyHashMap();
+                LinkedHashMap<String, MetaProperty> map = new LinkedHashMap<>();
                 map.put("length", arrayLengthProperty);
                 classPropertyIndex.put(theCachedClass, map);
             }
 
-            inheritStaticInterfaceFields(superClasses, new LinkedHashSet(interfaces));
+            inheritStaticInterfaceFields(superClasses, new LinkedHashSet<>(superInterfaces));
             inheritFields(superClasses);
 
             applyPropertyDescriptors(propertyDescriptors);
@@ -2428,11 +2426,9 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
     }
 
     private void makeStaticPropertyIndex() {
-        SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
-        for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext(); ) {
-            SingleKeyHashMap.Entry entry = ((SingleKeyHashMap.Entry) iter.next());
-
-            MetaProperty mp = (MetaProperty) entry.getValue();
+        LinkedHashMap<String, MetaProperty> propertyMap = classPropertyIndex.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
+        for (Map.Entry<String, MetaProperty> entry : propertyMap.entrySet()) {
+            MetaProperty mp = entry.getValue();
             if (mp instanceof CachedField) {
                 CachedField mfp = (CachedField) mp;
                 if (!mfp.isStatic()) continue;
@@ -2499,32 +2495,29 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         return result;
     }
 
-    private void copyClassPropertyIndexForSuper(Index dest) {
-        for (ComplexKeyHashMap.EntryIterator iter = classPropertyIndex.getEntrySetIterator(); iter.hasNext(); ) {
-            SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) iter.next();
-            SingleKeyHashMap newVal = new SingleKeyHashMap();
-            dest.put((CachedClass) entry.getKey(), newVal);
+    private void copyClassPropertyIndexForSuper(Map<CachedClass, LinkedHashMap<String, MetaProperty>> dest) {
+        for (Map.Entry<CachedClass, LinkedHashMap<String, MetaProperty>> entry : classPropertyIndex.entrySet()) {
+            LinkedHashMap<String, MetaProperty> newVal = new LinkedHashMap<>();
+            dest.put(entry.getKey(), newVal);
         }
     }
 
-    private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) {
-        for (Object anInterface : interfaces) {
-            CachedClass iclass = (CachedClass) anInterface;
-            SingleKeyHashMap iPropertyIndex = classPropertyIndex.getNotNull(iclass);
+    private void inheritStaticInterfaceFields(List<CachedClass> superClasses, Set<CachedClass> interfaces) {
+        for (CachedClass iclass : interfaces) {
+            LinkedHashMap<String, MetaProperty> iPropertyIndex = classPropertyIndex.computeIfAbsent(iclass, k -> new LinkedHashMap<>());
             addFields(iclass, iPropertyIndex);
-            for (Object superClass : superClasses) {
-                CachedClass sclass = (CachedClass) superClass;
-                if (!iclass.getTheClass().isAssignableFrom(sclass.getTheClass())) continue;
-                SingleKeyHashMap sPropertyIndex = classPropertyIndex.getNotNull(sclass);
+            for (CachedClass superClass : superClasses) {
+                if (!iclass.getTheClass().isAssignableFrom(superClass.getTheClass())) continue;
+                LinkedHashMap<String, MetaProperty> sPropertyIndex = classPropertyIndex.computeIfAbsent(superClass, k -> new LinkedHashMap<>());
                 copyNonPrivateFields(iPropertyIndex, sPropertyIndex, null);
             }
         }
     }
 
     private void inheritFields(LinkedList<CachedClass> superClasses) {
-        SingleKeyHashMap last = null;
+        LinkedHashMap<String, MetaProperty> last = null;
         for (CachedClass klass : superClasses) {
-            SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
+            LinkedHashMap<String, MetaProperty> propertyIndex = classPropertyIndex.computeIfAbsent(klass, k -> new LinkedHashMap<>());
             if (last != null) {
                 copyNonPrivateFields(last, propertyIndex, klass);
             }
@@ -2533,15 +2526,14 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         }
     }
 
-    private static void addFields(CachedClass klass, SingleKeyHashMap propertyIndex) {
+    private static void addFields(CachedClass klass, Map<String, MetaProperty> propertyIndex) {
         for (CachedField field : klass.getFields()) {
             propertyIndex.put(field.getName(), field);
         }
     }
 
-    private static void copyNonPrivateFields(SingleKeyHashMap from, SingleKeyHashMap to, @Nullable CachedClass klass) {
-        for (ComplexKeyHashMap.EntryIterator it = from.getEntrySetIterator(); it.hasNext(); ) {
-            SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) it.next();
+    private static void copyNonPrivateFields(Map<String, MetaProperty> from, Map<String, MetaProperty> to, @Nullable CachedClass klass) {
+        for (Map.Entry<String, MetaProperty> entry : from.entrySet()) {
             CachedField field = (CachedField) entry.getValue();
             int modifiers = field.getModifiers();
             if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers) || (!Modifier.isPrivate(modifiers)
@@ -2551,11 +2543,11 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         }
     }
 
-    private void applyStrayPropertyMethods(LinkedList<CachedClass> superClasses, Index classPropertyIndex, boolean isThis) {
+    private void applyStrayPropertyMethods(LinkedList<CachedClass> superClasses, Map<CachedClass, LinkedHashMap<String, MetaProperty>> classPropertyIndex, boolean isThis) {
         // now look for any stray getters that may be used to define a property
         for (CachedClass klass : superClasses) {
             MetaMethodIndex.Header header = metaMethodIndex.getHeader(klass.getTheClass());
-            SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
+            Map<String, MetaProperty> propertyIndex = classPropertyIndex.computeIfAbsent(klass, k -> new LinkedHashMap<>());
             for (MetaMethodIndex.Entry e = header.head; e != null; e = e.nextClassEntry) {
                 String methodName = e.name;
                 // name too short?
@@ -2639,9 +2631,9 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         }
     }
 
-    private static void createMetaBeanProperty(SingleKeyHashMap propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod) {
+    private static void createMetaBeanProperty(Map<String, MetaProperty> propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod) {
         // is this property already accounted for?
-        MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
+        MetaProperty mp = propertyIndex.get(propName);
         MetaProperty newMp = makeReplacementMetaProperty(mp, propName, isGetter, propertyMethod);
         if (newMp != mp) {
             propertyIndex.put(propName, newMp);
@@ -2695,10 +2687,10 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         if (staticProperty != null) {
             staticPropertyIndex.put(mp.getName(), mp);
         } else {
-            SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
+            LinkedHashMap<String, MetaProperty> propertyMap = classPropertyIndex.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
             //keep field
             CachedField field;
-            MetaProperty old = (MetaProperty) propertyMap.get(mp.getName());
+            MetaProperty old = propertyMap.get(mp.getName());
             if (old != null) {
                 if (old instanceof MetaBeanProperty) {
                     field = ((MetaBeanProperty) old).getField();
@@ -2892,13 +2884,13 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
 
         CachedClass clazz = ReflectionCache.getCachedClass(_clazz);
         while (true) {
-            SingleKeyHashMap propertyMap;
+            Map<String, MetaProperty> propertyMap;
             if (useStatic) {
                 propertyMap = staticPropertyIndex;
             } else if (useSuper) {
-                propertyMap = classPropertyIndexForSuper.getNullable(clazz);
+                propertyMap = classPropertyIndexForSuper.get(clazz);
             } else {
-                propertyMap = classPropertyIndex.getNullable(clazz);
+                propertyMap = classPropertyIndex.get(clazz);
             }
             if (propertyMap == null) {
                 if (clazz != theCachedClass) {
@@ -2908,22 +2900,22 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
                     return null;
                 }
             }
-            return (MetaProperty) propertyMap.get(name);
+            return propertyMap.get(name);
         }
     }
 
     private MetaProperty getMetaProperty(String name, boolean useStatic) {
         CachedClass clazz = theCachedClass;
-        SingleKeyHashMap propertyMap;
+        Map<String, MetaProperty> propertyMap;
         if (useStatic) {
             propertyMap = staticPropertyIndex;
         } else {
-            propertyMap = classPropertyIndex.getNullable(clazz);
+            propertyMap = classPropertyIndex.get(clazz);
         }
         if (propertyMap == null) {
             return null;
         }
-        return (MetaProperty) propertyMap.get(name);
+        return propertyMap.get(name);
     }
 
     /**
@@ -3938,6 +3930,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         metaMethodIndex.clearCaches();
     }
 
+    @Deprecated
     private static final SingleKeyHashMap.Copier NAME_INDEX_COPIER = value -> {
         if (value instanceof FastArray) {
             return ((FastArray) value).copy();
@@ -3946,8 +3939,13 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         }
     };
 
+    @Deprecated
     private static final SingleKeyHashMap.Copier METHOD_INDEX_COPIER = value -> SingleKeyHashMap.copy(new SingleKeyHashMap(false), (SingleKeyHashMap) value, NAME_INDEX_COPIER);
 
+    /**
+     * @deprecated use {@link LinkedHashMap} instead
+     */
+    @Deprecated
     static class MethodIndex extends Index {
         public MethodIndex(boolean b) {
             super(false);
@@ -3970,6 +3968,10 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         }
     }
 
+    /**
+     * @deprecated use {@link LinkedHashMap} instead
+     */
+    @Deprecated
     public static class Index extends SingleKeyHashMap {
 
         public Index(int size) {
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
index 17bcc8d..54b320f 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
@@ -52,7 +52,7 @@ import org.codehaus.groovy.runtime.GeneratedClosure;
 import org.codehaus.groovy.runtime.GeneratedLambda;
 import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
 import org.codehaus.groovy.transform.trait.Traits;
-import org.codehaus.groovy.util.ManagedConcurrentMap;
+import org.codehaus.groovy.util.ManagedIdentityConcurrentMap;
 import org.codehaus.groovy.util.ReferenceBundle;
 import org.codehaus.groovy.vmplugin.VMPluginFactory;
 import org.objectweb.asm.Opcodes;
@@ -408,7 +408,7 @@ public class ClassHelper {
     }
 
     static class ClassHelperCache {
-        static ManagedConcurrentMap<Class, SoftReference<ClassNode>> classCache = new ManagedConcurrentMap<Class, SoftReference<ClassNode>>(ReferenceBundle.getWeakBundle());
+        static ManagedIdentityConcurrentMap<Class, SoftReference<ClassNode>> classCache = new ManagedIdentityConcurrentMap<Class, SoftReference<ClassNode>>(ReferenceBundle.getWeakBundle());
     }
 
     public static boolean isSAMType(final ClassNode type) {
diff --git a/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java b/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java
index e55b2ed..b0c00e1 100644
--- a/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java
+++ b/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java
@@ -46,7 +46,7 @@ import org.codehaus.groovy.util.Finalizable;
 import org.codehaus.groovy.util.LazyReference;
 import org.codehaus.groovy.util.LockableObject;
 import org.codehaus.groovy.util.ManagedConcurrentLinkedQueue;
-import org.codehaus.groovy.util.ManagedConcurrentMap;
+import org.codehaus.groovy.util.ManagedIdentityConcurrentMap;
 import org.codehaus.groovy.util.ManagedReference;
 import org.codehaus.groovy.util.ReferenceBundle;
 import org.codehaus.groovy.vmplugin.VMPluginFactory;
@@ -82,7 +82,7 @@ public class ClassInfo implements Finalizable {
     private ManagedReference<MetaClass> weakMetaClass;
     MetaMethod[] dgmMetaMethods = MetaMethod.EMPTY_ARRAY;
     MetaMethod[] newMetaMethods = MetaMethod.EMPTY_ARRAY;
-    private ManagedConcurrentMap<Object, MetaClass> perInstanceMetaClassMap;
+    private ManagedIdentityConcurrentMap<Object, MetaClass> perInstanceMetaClassMap;
 
     private static final ReferenceBundle softBundle = ReferenceBundle.getSoftBundle();
     private static final ReferenceBundle weakBundle = ReferenceBundle.getWeakBundle();
@@ -409,7 +409,7 @@ public class ClassInfo implements Finalizable {
 
         if (metaClass != null) {
             if (perInstanceMetaClassMap == null)
-              perInstanceMetaClassMap = new ManagedConcurrentMap<Object, MetaClass>(ReferenceBundle.getWeakBundle());
+              perInstanceMetaClassMap = new ManagedIdentityConcurrentMap<Object, MetaClass>(ReferenceBundle.getWeakBundle());
 
             perInstanceMetaClassMap.put(obj, metaClass);
         }
diff --git a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java
index 02f893c..7b95d64 100644
--- a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java
+++ b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java
@@ -28,6 +28,7 @@ import org.codehaus.groovy.util.ReferenceBundle;
  *
  * @param <T>
  */
+@Deprecated
 class GroovyClassValuePreJava7<T> implements GroovyClassValue<T> {
 	private static final ReferenceBundle weakBundle = ReferenceBundle.getWeakBundle();
 
diff --git a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
index afaf956..f9763e1 100644
--- a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
+++ b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
@@ -32,7 +32,7 @@ import org.codehaus.groovy.runtime.metaclass.MixedInMetaClass;
 import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod;
 import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaProperty;
 import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
-import org.codehaus.groovy.util.ManagedConcurrentMap;
+import org.codehaus.groovy.util.ManagedIdentityConcurrentMap;
 import org.codehaus.groovy.util.ReferenceBundle;
 
 import java.lang.reflect.Modifier;
@@ -40,19 +40,18 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
-public class MixinInMetaClass extends ManagedConcurrentMap {
+public class MixinInMetaClass {
     final ExpandoMetaClass emc;
     final CachedClass mixinClass;
     final CachedConstructor constructor;
-
-    private static final ReferenceBundle softBundle = ReferenceBundle.getSoftBundle();
+    private final ManagedIdentityConcurrentMap managedIdentityConcurrentMap =
+            new ManagedIdentityConcurrentMap(ReferenceBundle.getSoftBundle());
 
     public MixinInMetaClass(ExpandoMetaClass emc, CachedClass mixinClass) {
-        super(softBundle);
         this.emc = emc;
         this.mixinClass = mixinClass;
 
-        constructor = findDefaultConstructor(mixinClass);
+        this.constructor = findDefaultConstructor(mixinClass);
         emc.addMixinClass(this);
     }
 
@@ -70,20 +69,20 @@ public class MixinInMetaClass extends ManagedConcurrentMap {
     }
 
     public synchronized Object getMixinInstance(Object object) {
-        Object mixinInstance = get(object);
+        Object mixinInstance = managedIdentityConcurrentMap.get(object);
         if (mixinInstance == null) {
             mixinInstance = constructor.invoke(MetaClassHelper.EMPTY_ARRAY);
             new MixedInMetaClass(mixinInstance, object);
-            put(object, mixinInstance);
+            managedIdentityConcurrentMap.put(object, mixinInstance);
         }
         return mixinInstance;
     }
 
     public synchronized void setMixinInstance(Object object, Object mixinInstance) {
         if (mixinInstance == null) {
-            remove(object);
+            managedIdentityConcurrentMap.remove(object);
         } else {
-            put(object, mixinInstance);
+            managedIdentityConcurrentMap.put(object, mixinInstance);
         }
     }
 
diff --git a/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java b/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java
index 7f6c525..2b33315 100644
--- a/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java
+++ b/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java
@@ -26,8 +26,10 @@ public class ReflectionCache {
         return TypeUtil.autoboxType(type);
     }
 
+    @Deprecated
     static TripleKeyHashMap mopNames = new TripleKeyHashMap();
 
+    @Deprecated // the method is never called
     public static String getMOPMethodName(CachedClass declaringClass, String name, boolean useThis) {
         TripleKeyHashMap.Entry mopNameEntry = mopNames.getOrPut(declaringClass, name, useThis);
         if (mopNameEntry.value == null) {
diff --git a/src/main/java/org/codehaus/groovy/runtime/memoize/MemoizeCache.java b/src/main/java/org/codehaus/groovy/runtime/memoize/MemoizeCache.java
index c525919..dfa4438 100644
--- a/src/main/java/org/codehaus/groovy/runtime/memoize/MemoizeCache.java
+++ b/src/main/java/org/codehaus/groovy/runtime/memoize/MemoizeCache.java
@@ -44,7 +44,7 @@ public interface MemoizeCache<K, V> {
      * Try to get the value from cache.
      * If not found, create the value by {@link ValueProvider} and put it into the cache, at last return the value.
      *
-     * @param key
+     * @param key the key to look up
      * @param valueProvider provide the value if the associated value not found
      * @return the cached value
      */
diff --git a/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java b/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java
index 5074908..8cae32f 100644
--- a/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java
+++ b/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java
@@ -23,7 +23,7 @@ import groovy.lang.MetaBeanProperty;
 import groovy.lang.MetaMethod;
 import org.codehaus.groovy.reflection.CachedClass;
 import org.codehaus.groovy.reflection.ReflectionCache;
-import org.codehaus.groovy.util.ManagedConcurrentMap;
+import org.codehaus.groovy.util.ManagedIdentityConcurrentMap;
 import org.codehaus.groovy.util.ReferenceBundle;
 
 import java.lang.reflect.Modifier;
@@ -39,9 +39,9 @@ import java.util.concurrent.ConcurrentHashMap;
  * @since 1.5
  */
 public class ThreadManagedMetaBeanProperty extends MetaBeanProperty {
-    private static final ConcurrentHashMap<String,ManagedConcurrentMap> PROPNAME_TO_MAP = new ConcurrentHashMap<String, ManagedConcurrentMap>();
+    private static final ConcurrentHashMap<String,ManagedIdentityConcurrentMap> PROPNAME_TO_MAP = new ConcurrentHashMap<String, ManagedIdentityConcurrentMap>();
 
-    private final ManagedConcurrentMap instance2Prop;
+    private final ManagedIdentityConcurrentMap instance2Prop;
 
     private final Class declaringClass;
     private final ThreadBoundGetter getter;
@@ -117,11 +117,11 @@ public class ThreadManagedMetaBeanProperty extends MetaBeanProperty {
         instance2Prop = getInstance2PropName(name);
     }
 
-    private static ManagedConcurrentMap getInstance2PropName(String name) {
-        ManagedConcurrentMap res = PROPNAME_TO_MAP.get(name);
+    private static ManagedIdentityConcurrentMap getInstance2PropName(String name) {
+        ManagedIdentityConcurrentMap res = PROPNAME_TO_MAP.get(name);
         if (res == null) {
-            res = new ManagedConcurrentMap(SOFT_BUNDLE);
-            ManagedConcurrentMap ores = PROPNAME_TO_MAP.putIfAbsent(name, res);
+            res = new ManagedIdentityConcurrentMap(SOFT_BUNDLE);
+            ManagedIdentityConcurrentMap ores = PROPNAME_TO_MAP.putIfAbsent(name, res);
             if (ores != null)
               return ores;
         }
@@ -176,7 +176,7 @@ public class ThreadManagedMetaBeanProperty extends MetaBeanProperty {
            * @see groovy.lang.MetaMethod#invoke(java.lang.Object, java.lang.Object[])
            */
         public Object invoke(Object object, Object[] arguments) {
-            return instance2Prop.getOrPut(object, getInitialValue()).getValue();
+            return instance2Prop.getOrPut(object, getInitialValue());
         }
     }
 
diff --git a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java
index 6168877..d67fd88 100644
--- a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java
+++ b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java
@@ -18,6 +18,7 @@
  */
 package org.codehaus.groovy.util;
 
+@Deprecated
 public abstract class AbstractConcurrentMap<K, V> extends AbstractConcurrentMapBase {
 
     public AbstractConcurrentMap(Object segmentInfo) {
diff --git a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java
index 01f84af..57ac68e 100644
--- a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java
+++ b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java
@@ -21,6 +21,7 @@ package org.codehaus.groovy.util;
 import java.util.Collection;
 import java.util.LinkedList;
 
+@Deprecated
 public abstract class AbstractConcurrentMapBase {
     protected static final int MAXIMUM_CAPACITY = 1 << 30;
     static final int MAX_SEGMENTS = 1 << 16;
diff --git a/src/main/java/org/codehaus/groovy/util/ComplexKeyHashMap.java b/src/main/java/org/codehaus/groovy/util/ComplexKeyHashMap.java
index c74f0bb..5d8a1b8 100644
--- a/src/main/java/org/codehaus/groovy/util/ComplexKeyHashMap.java
+++ b/src/main/java/org/codehaus/groovy/util/ComplexKeyHashMap.java
@@ -21,6 +21,7 @@ package org.codehaus.groovy.util;
 import java.util.Arrays;
 import java.util.NoSuchElementException;
 
+@Deprecated
 public class ComplexKeyHashMap
 {
   public static class Entry {
diff --git a/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java b/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java
index d1cf760..7329711 100644
--- a/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java
+++ b/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java
@@ -18,6 +18,7 @@
  */
 package org.codehaus.groovy.util;
 
+@Deprecated
 public class ManagedConcurrentMap<K,V> extends AbstractConcurrentMap<K,V> {
     protected ReferenceBundle bundle;
     public ManagedConcurrentMap(ReferenceBundle bundle) {
diff --git a/src/main/java/org/codehaus/groovy/util/ManagedIdentityConcurrentMap.java b/src/main/java/org/codehaus/groovy/util/ManagedIdentityConcurrentMap.java
new file mode 100644
index 0000000..2736244
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/util/ManagedIdentityConcurrentMap.java
@@ -0,0 +1,149 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.util;
+
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This is a basic implementation of a map able to forget its keys. This
+ * map uses internally a ConcurrentHashMap, thus should be safe for concurrency.
+ * hashcode and equals are used to find the entries and should thus be implemented
+ * properly for the keys. This map does not support null keys.
+ *
+ * @param <K> the key type
+ * @param <V> the value type
+ * @since 4.0.0
+ */
+public class ManagedIdentityConcurrentMap<K, V> {
+    private final ConcurrentHashMap<ManagedReference<K>, V> internalMap;
+    private final ReferenceBundle bundle;
+
+    public ManagedIdentityConcurrentMap(ReferenceBundle bundle) {
+        this.internalMap = new ConcurrentHashMap<>();
+        this.bundle = bundle;
+    }
+
+    /**
+     * Returns the value stored for the given key at the point of call.
+     *
+     * @param key a non null key
+     * @return the value stored in the map for the given key
+     */
+    public V get(K key) {
+        return internalMap.get(new ManagedIdentityKey<K>(key));
+    }
+
+    /**
+     * Sets a new value for a given key. an older value is overwritten.
+     *
+     * @param key   a non null key
+     * @param value the new value
+     */
+    public V put(final K key, V value) {
+        return internalMap.put(new ManagedIdentityKey<K>(key), value);
+    }
+
+    /**
+     * Returns the values of the map
+     */
+    public Collection<V> values() {
+        return internalMap.values();
+    }
+
+    /**
+     * Remove the key specified entry
+     *
+     * @param key the key to look up
+     * @return the removed value
+     */
+    public V remove(K key) {
+        return internalMap.remove(new ManagedIdentityKey<K>(key));
+    }
+
+    /**
+     * Get the key specified value, or put the default value and return it if the key is absent
+     *
+     * @param key the key to look up
+     * @param value the default value if the key is absent
+     * @return the value
+     */
+    public V getOrPut(K key, V value) {
+        return internalMap.computeIfAbsent(new ManagedIdentityKey<K>(key), k -> value);
+    }
+
+    /**
+     * Returns the map size
+     */
+    public Object size() {
+        return internalMap.size();
+    }
+
+    /**
+     * Check if the map is empty or not
+     *
+     * @return {@code true} when the map is empty, otherwise {@code false}
+     */
+    public boolean isEmpty() {
+        return internalMap.isEmpty();
+    }
+
+    /**
+     * Represents identity key of {@link ManagedIdentityConcurrentMap}
+     *
+     * @param <K> the key type
+     */
+    private class ManagedIdentityKey<K> extends ManagedReference<K> {
+        private final int hashCode;
+
+        private ManagedIdentityKey(K key) {
+            super(bundle, key);
+            this.hashCode = hash(key);
+        }
+
+        @Override
+        public void finalizeReference() {
+            internalMap.remove(this);
+            super.finalizeReference();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof ManagedIdentityKey)) return false;
+            return get() == ((ManagedIdentityKey<?>) o).get();
+        }
+
+        @Override
+        public int hashCode() {
+            return this.hashCode;
+        }
+
+        private int hash(K key) {
+            int h = (null == key) ? 0 : System.identityHashCode(key);
+            h += (h << 15) ^ 0xffffcd7d;
+            h ^= (h >>> 10);
+            h += (h << 3);
+            h ^= (h >>> 6);
+            h += (h << 2) + (h << 14);
+            h ^= (h >>> 16);
+            return h;
+        }
+    }
+}
diff --git a/src/main/java/org/codehaus/groovy/util/SingleKeyHashMap.java b/src/main/java/org/codehaus/groovy/util/SingleKeyHashMap.java
index 974b24b..86e0990 100644
--- a/src/main/java/org/codehaus/groovy/util/SingleKeyHashMap.java
+++ b/src/main/java/org/codehaus/groovy/util/SingleKeyHashMap.java
@@ -18,6 +18,7 @@
  */
 package org.codehaus.groovy.util;
 
+@Deprecated
 public class SingleKeyHashMap extends ComplexKeyHashMap
 {
     public static class Entry extends ComplexKeyHashMap.Entry{
diff --git a/src/main/java/org/codehaus/groovy/util/TripleKeyHashMap.java b/src/main/java/org/codehaus/groovy/util/TripleKeyHashMap.java
index b2d891b..668db9a 100644
--- a/src/main/java/org/codehaus/groovy/util/TripleKeyHashMap.java
+++ b/src/main/java/org/codehaus/groovy/util/TripleKeyHashMap.java
@@ -18,6 +18,7 @@
  */
 package org.codehaus.groovy.util;
 
+@Deprecated
 public class TripleKeyHashMap extends ComplexKeyHashMap
 {
   public static class Entry extends ComplexKeyHashMap.Entry{
diff --git a/src/test/org/codehaus/groovy/util/ManagedIdentityConcurrentMapTest.groovy b/src/test/org/codehaus/groovy/util/ManagedIdentityConcurrentMapTest.groovy
new file mode 100644
index 0000000..d454b3f
--- /dev/null
+++ b/src/test/org/codehaus/groovy/util/ManagedIdentityConcurrentMapTest.groovy
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.util
+
+
+import org.junit.Test
+
+class ManagedIdentityConcurrentMapTest {
+    @Test
+    void testRemovingEntriesFromMapAfterGC() {
+        def m = new ManagedIdentityConcurrentMap<Object, String>(ReferenceBundle.getWeakBundle())
+        def k1 = new Object()
+        m.put(k1, "a")
+        def k2 = new Object()
+        m.put(k2, "b")
+        def k3 = new Object()
+        m.put(k3, "c")
+
+        assert 3 == m.size()
+
+        // the related entries should be removed after GC happens
+        k1 = null
+        k2 = null
+        k3 = null
+
+        // finalize via GC, which is hard to predicate though it will happen at last
+        for (int i = 0; i < 20; i++) {
+            System.gc()
+
+            if (m.values().size() == 0) {
+                break
+            }
+
+            Thread.sleep(100)
+        }
+
+        // finalize manually
+        if (!m.isEmpty()) {
+            m.internalMap.keySet().stream().forEach(e -> e.finalizeReference())
+        }
+
+        assert m.isEmpty()
+    }
+}
diff --git a/subprojects/groovy-json/src/spec/test/json/JsonTest.groovy b/subprojects/groovy-json/src/spec/test/json/JsonTest.groovy
index 1069af1..02b6863 100644
--- a/subprojects/groovy-json/src/spec/test/json/JsonTest.groovy
+++ b/subprojects/groovy-json/src/spec/test/json/JsonTest.groovy
@@ -120,7 +120,7 @@ class JsonTest extends GroovyTestCase {
             .excludeFieldsByType(URL)
             .build()
 
-        assert generator.toJson(person) == '{"dob":"1984@12","name":"John"}'
+        assert generator.toJson(person) == '{"name":"John","dob":"1984@12"}'
         // end::json_output_generator[]
         '''
     }
@@ -148,7 +148,7 @@ class JsonTest extends GroovyTestCase {
             }
             .build()
 
-        assert generator.toJson(person) == '{"favoriteUrl":"groovy-lang.org","name":"John"}'
+        assert generator.toJson(person) == '{"name":"John","favoriteUrl":"groovy-lang.org"}'
 
         // No key available when generating a JSON Array
         def list = [new URL('http://groovy-lang.org/json.html#_jsonoutput')]
diff --git a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
index 3ed54bf..20c061d 100644
--- a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
+++ b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
@@ -306,30 +306,30 @@ class JsonOutputTest extends GroovyTestCase {
                 "name": "Paris",
                 "districts": [
                     {
+                        "number": 1,
                         "streets": [
                             {
-                                "kind": "street",
-                                "streetName": "Saint-Honore"
+                                "streetName": "Saint-Honore",
+                                "kind": "street"
                             },
                             {
-                                "kind": "avenue",
-                                "streetName": "de l'Opera"
+                                "streetName": "de l'Opera",
+                                "kind": "avenue"
                             }
-                        ],
-                        "number": 1
+                        ]
                     },
                     {
+                        "number": 2,
                         "streets": [
                             {
-                                "kind": "boulevard",
-                                "streetName": "des Italiens"
+                                "streetName": "des Italiens",
+                                "kind": "boulevard"
                             },
                             {
-                                "kind": "boulevard",
-                                "streetName": "Bonne Nouvelle"
+                                "streetName": "Bonne Nouvelle",
+                                "kind": "boulevard"
                             }
-                        ],
-                        "number": 2
+                        ]
                     }
                 ]
             }'''.stripIndent()
@@ -350,12 +350,12 @@ class JsonOutputTest extends GroovyTestCase {
     void testObjectWithDeclaredPropertiesField() {
         def person = new JsonObject(name: "pillow", properties: [state: "fluffy", color: "white"])
         def json = toJson(person)
-        assert json == '{"properties":{"state":"fluffy","color":"white"},"name":"pillow"}'
+        assert json == '{"name":"pillow","properties":{"state":"fluffy","color":"white"}}'
     }
 
     void testGROOVY5494() {
         def json = toJson(new JsonFoo(name: "foo"))
-        assert json == '{"properties":0,"name":"foo"}'
+        assert json == '{"name":"foo","properties":0}'
     }
 
     void testCharacter() {