]> source.dussan.org Git - aspectj.git/commitdiff
Optimize class loading
authorStefan Starke <stefan@starkeweb.org>
Tue, 8 Oct 2019 11:27:41 +0000 (13:27 +0200)
committerAndy Clement <aclement@pivotal.io>
Thu, 13 Jan 2022 23:07:30 +0000 (15:07 -0800)
In our project we found out that during the build up of the spring context
the class loading takes a very long time.
Root cause is the huge amount of file I/O during pointcut class loading.
We are taking about ~250k file loads.

With these changes we managed to cut down the starting time by around 50%.

What we found out is that IMHO - the clear method of the ClassLoaderRepository
is called far too often -> in our settings this resulted in not a single cache
hit as the cache got cleared permanently.
Therefore we de-actived the cache clear calls inside the ClassLoaderRepository.

Secondly we changed the Java15AnnotationFinder in a way to not always create
new objects for the ClassLoaderRepository but re-use one static instance.
Otherwise we experienced >100k objects being created.

Last but not least we introduced a cache for unavailable classes so that
they do not have to be looked up using file I/O over and over again.

The whole behavior is configurable via
+ org.aspectj.apache.bcel.useSingleRepositoryInstance (default: true)
+ org.aspectj.apache.bcel.useUnavailableClassesCache (default: true)
+ org.aspectj.apache.bcel.ignoreCacheClearRequests (default: true)

Signed-off-by: Stefan Starke <stefan@starkeweb.org>
Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
bcel-builder/src/main/java/org/aspectj/apache/bcel/util/ClassLoaderRepository.java
weaver/src/main/java/org/aspectj/weaver/reflect/Java15AnnotationFinder.java

index e16d0efff3d74770bcd6c38534c5ee34e532718d..cdc072b5c03b5bb5efe50523d2aa9c1245e03618 100644 (file)
@@ -64,6 +64,7 @@ import java.net.URLClassLoader;
 import java.util.AbstractMap;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.WeakHashMap;
@@ -95,6 +96,16 @@ public class ClassLoaderRepository implements Repository {
 
        public static boolean useSharedCache = System.getProperty("org.aspectj.apache.bcel.useSharedCache", "true").equalsIgnoreCase("true");
 
+       //Cache not found classes as well to prevent unnecessary file I/O operations
+       public static final boolean useUnavailableClassesCache =
+               System.getProperty("org.aspectj.apache.bcel.useUnavailableClassesCache", "true").equalsIgnoreCase("true");
+       //Ignore cache clear requests to not build up the cache over and over again
+       public static final boolean ignoreCacheClearRequests =
+               System.getProperty("org.aspectj.apache.bcel.ignoreCacheClearRequests", "true").equalsIgnoreCase("true");
+
+       //Second cache for the unavailable classes
+       private static Set<String> unavailableClasses = new HashSet<String>();
+
        private static int cacheHitsShared = 0;
        private static int missSharedEvicted = 0; // Misses in shared cache access due to reference GC
        private long timeManipulatingURLs = 0L;
@@ -183,8 +194,10 @@ public class ClassLoaderRepository implements Repository {
 
                @Override
                public void clear() {
-                       processQueue();
-                       map.clear();
+                       if (!ignoreCacheClearRequests) {
+                               processQueue();
+                               map.clear();
+                       }
                }
 
                @Override
@@ -281,12 +294,19 @@ public class ClassLoaderRepository implements Repository {
         */
        public JavaClass loadClass(String className) throws ClassNotFoundException {
 
+               //Quick evaluation of unavailable classes to prevent unnecessary file I/O
+               if (useUnavailableClassesCache && unavailableClasses.contains(className))
+                       throw new ClassNotFoundException(className + " not found.");
+
                // translate to a URL
                long time = System.currentTimeMillis();
                java.net.URL url = toURL(className);
                timeManipulatingURLs += (System.currentTimeMillis() - time);
-               if (url == null)
+               if (url == null) {
+                       if (useUnavailableClassesCache)
+                               unavailableClasses.add(className);
                        throw new ClassNotFoundException(className + " not found - unable to determine URL");
+               }
 
                JavaClass clazz = null;
 
@@ -314,6 +334,9 @@ public class ClassLoaderRepository implements Repository {
                        InputStream is = (useSharedCache ? url.openStream() : loaderRef.getClassLoader().getResourceAsStream(
                                        classFile + ".class"));
                        if (is == null) {
+                               if (useUnavailableClassesCache) {
+                                       unavailableClasses.add(className);
+                               }
                                throw new ClassNotFoundException(className + " not found using url " + url);
                        }
                        ClassParser parser = new ClassParser(is, className);
@@ -326,6 +349,7 @@ public class ClassLoaderRepository implements Repository {
                        classesLoadedCount++;
                        return clazz;
                } catch (IOException e) {
+                       unavailableClasses.add(className);
                        throw new ClassNotFoundException(e.toString());
                }
        }
@@ -384,10 +408,12 @@ public class ClassLoaderRepository implements Repository {
 
        /** Clear all entries from the local cache */
        public void clear() {
-               if (useSharedCache)
-                       sharedCache.clear();
-               else
-                       localCache.clear();
+               if (!ignoreCacheClearRequests) {
+                       if (useSharedCache)
+                               sharedCache.clear();
+                       else
+                               localCache.clear();
+               }
        }
 
 }
index dfddd18da673a5ba62a3318bc8ad792c8a3b7601..ef437b724d14726dc28e150e8608ced5001a366b 100644 (file)
@@ -42,9 +42,17 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
 
        private Repository bcelRepository;
        private BcelWeakClassLoaderReference classLoaderRef;
+
+       private static Repository staticBcelRepository;
+       private static BcelWeakClassLoaderReference staticClassLoaderRef;
+
        private World world;
        private static boolean useCachingClassLoaderRepository;
 
+       //Use single instance of Repository and ClassLoader
+       public static final boolean useSingleInstances =
+               System.getProperty("org.aspectj.apache.bcel.useSingleRepositoryInstance", "true").equalsIgnoreCase("true");
+
        static {
                try {
                        useCachingClassLoaderRepository = System.getProperty("Xset:bcelRepositoryCaching","true").equalsIgnoreCase("true");
@@ -58,14 +66,31 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
        }
 
        public void setClassLoader(ClassLoader aLoader) {
-               this.classLoaderRef = new BcelWeakClassLoaderReference(aLoader);
+               //Set class loader ref
+               if (useSingleInstances && staticClassLoaderRef == null)
+                       staticClassLoaderRef = new BcelWeakClassLoaderReference(aLoader);
+               else
+                       this.classLoaderRef = new BcelWeakClassLoaderReference(aLoader);
+
+               //Set repository
                if (useCachingClassLoaderRepository) {
-                       this.bcelRepository = new ClassLoaderRepository(classLoaderRef);
-               } else {
-                       this.bcelRepository = new NonCachingClassLoaderRepository(classLoaderRef);
+                       if (useSingleInstances && staticBcelRepository == null)
+                               staticBcelRepository = new ClassLoaderRepository(getClassLoader());
+                       else
+                               this.bcelRepository = new ClassLoaderRepository(getClassLoader());
+               }
+               else {
+                       if (useSingleInstances && staticBcelRepository == null)
+                               staticBcelRepository = new NonCachingClassLoaderRepository(getClassLoader());
+                       else
+                               this.bcelRepository = new NonCachingClassLoaderRepository(getClassLoader());
                }
        }
 
+       private Repository getBcelRepository() {
+               return useSingleInstances ? staticBcelRepository : bcelRepository;
+       }
+
        public void setWorld(World aWorld) {
                this.world = aWorld;
        }
@@ -112,7 +137,7 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
        }
 
        private ClassLoader getClassLoader() {
-               return classLoaderRef.getClassLoader();
+               return useSingleInstances ? staticClassLoaderRef.getClassLoader() : classLoaderRef.getClassLoader();
        }
 
        public AnnotationAJ getAnnotationOfType(UnresolvedType ofType, Member onMember) {
@@ -145,7 +170,7 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                                anns = bcelField.getAnnotations();
                        }
                        // the answer is cached and we don't want to hold on to memory
-                       bcelRepository.clear();
+                       getBcelRepository().clear();
                        // OPTIMIZE make constant 0 size array for sharing
                        if (anns == null)
                                anns = AnnotationGen.NO_ANNOTATIONS;
@@ -164,7 +189,7 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
 
        public String getAnnotationDefaultValue(Member onMember) {
                try {
-                       JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass());
+                       JavaClass jc = getBcelRepository().loadClass(onMember.getDeclaringClass());
                        if (onMember instanceof Method) {
                                org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember);
 
@@ -200,7 +225,7 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                // we can just use reflection.
                if (!areRuntimeAnnotationsSufficient) {
                        try {
-                               JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass());
+                               JavaClass jc = getBcelRepository().loadClass(onMember.getDeclaringClass());
                                org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = null;
                                if (onMember instanceof Method) {
                                        org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember);
@@ -215,7 +240,7 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                                        anns = bcelField.getAnnotations();
                                }
                                // the answer is cached and we don't want to hold on to memory
-                               bcelRepository.clear();
+                               getBcelRepository().clear();
                                if (anns == null || anns.length == 0) {
                                        return ResolvedType.NONE;
                                }
@@ -246,9 +271,9 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                // annotations so we bail out to Bcel and then chuck away the JavaClass so that we
                // don't hog memory.
                try {
-                       JavaClass jc = bcelRepository.loadClass(forClass);
+                       JavaClass jc = getBcelRepository().loadClass(forClass);
                        org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = jc.getAnnotations();
-                       bcelRepository.clear();
+                       getBcelRepository().clear();
                        if (anns == null) {
                                return ResolvedType.NONE;
                        } else {
@@ -276,7 +301,7 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                        return null;
 
                try {
-                       JavaClass jc = bcelRepository.loadClass(forMember.getDeclaringClass());
+                       JavaClass jc = getBcelRepository().loadClass(forMember.getDeclaringClass());
                        LocalVariableTable lvt = null;
                        int numVars = 0;
                        if (forMember instanceof Method) {
@@ -320,7 +345,7 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                // don't hog
                // memory.
                try {
-                       JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass());
+                       JavaClass jc = getBcelRepository().loadClass(onMember.getDeclaringClass());
                        org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[][] anns = null;
                        if (onMember instanceof Method) {
                                org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember);
@@ -340,7 +365,7 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                                // anns = null;
                        }
                        // the answer is cached and we don't want to hold on to memory
-                       bcelRepository.clear();
+                       getBcelRepository().clear();
                        if (anns == null)
                                return NO_PARAMETER_ANNOTATIONS;
                        ResolvedType[][] result = new ResolvedType[anns.length][];