Commits

Anonymous committed fa747d1

Improved performance of PackageObjectFactory:

* Check for class existence in a saner order
* Cache missed existence checks to avoid
expensive classpath re-scans

Comments (0)

Files changed (1)

src/checkstyle/com/puppycrawl/tools/checkstyle/PackageObjectFactory.java

 ////////////////////////////////////////////////////////////////////////////////
 package com.puppycrawl.tools.checkstyle;
 
+import com.google.common.base.Function;
+import com.google.common.collect.MapMaker;
 import com.google.common.collect.Sets;
 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
+import java.util.Map;
 import java.util.Set;
 
 /**
  */
 class PackageObjectFactory implements ModuleFactory
 {
+    /** A sentinel indicating a CNFE */
+    private static class Miss
+    {
+    }
+
+    /**
+     * A cache from classloader to class cache. Ensures cached results
+     * (especially misses) outlive any particular PackageObjectFactory.
+     */
+    private static final
+    Map<ClassLoader, Map<String, Class<?>>> CLASSLOADER_CACHE =
+        new MapMaker()
+            .makeComputingMap(new Function<ClassLoader, Map<String, Class<?>>>()
+            {
+                public Map<String, Class<?>> apply(ClassLoader aClassLoader)
+                {
+                    final int nProcs =
+                        Runtime.getRuntime().availableProcessors();
+                    return new MapMaker()
+                        .concurrencyLevel(2 * nProcs)
+                        .makeMap();
+                }
+            });
+
     /** a list of package names to prepend to class names */
     private final Set<String> mPackages;
 
     private final ClassLoader mModuleClassLoader;
 
     /**
+     * A map from name to class object; especially useful because of negative
+     * caching.
+     *
+     * Thread-safe as long as the classloader returns identical (or
+     * interchangeable) Class objects for repeated identical calls to
+     * Class.forName.
+     */
+    private final Map<String, Class<?>> mClassCache;
+
+    /**
      * Creates a new <code>PackageObjectFactory</code> instance.
      * @param aPackageNames the list of package names to use
      * @param aModuleClassLoader class loader used to load Checkstyle
         //create a copy of the given set, but retain ordering
         mPackages = Sets.newLinkedHashSet(aPackageNames);
         mModuleClassLoader = aModuleClassLoader;
+        mClassCache = CLASSLOADER_CACHE.get(mModuleClassLoader);
     }
 
     /**
         throws CheckstyleException
     {
         try {
-            final Class<?> clazz = Class.forName(aClassName, true,
+            final Class<?> cached = mClassCache.get(aClassName);
+            if (cached == null) {
+                final Class<?> clazz = Class.forName(aClassName, true,
                     mModuleClassLoader);
-            return clazz.newInstance();
+                mClassCache.put(aClassName, clazz);
+                return clazz.newInstance();
+            }
+            else if (cached != Miss.class) {
+                return cached.newInstance();
+            }
+            else {
+                throw new CheckstyleException(
+                    "Unable to find class for " + aClassName);
+            }
+
         }
         catch (final ClassNotFoundException e) {
+            mClassCache.put(aClassName, Miss.class);
             throw new CheckstyleException(
                 "Unable to find class for " + aClassName, e);
         }
         throws CheckstyleException
     {
         try {
-            return doMakeObject(aName);
+            // We expect to make Check objects far more often than non-Checks.
+            return doMakeObject(aName + "Check");
         }
         catch (final CheckstyleException ex) {
-            //try again with suffix "Check"
+            //try again without suffix "Check"
             try {
-                return doMakeObject(aName + "Check");
+                return doMakeObject(aName);
             }
             catch (final CheckstyleException ex2) {
                 throw new CheckstyleException(