Skip to content

Startup performance regression due to CGLIB class load attempts in Spring 6.1.x #34677

@rahulsh1

Description

@rahulsh1

A considerable startup regression over 30+ seconds was found in the Spring refresh phase after the migration to Spring 6.1.16 from Spring 5.3.x with no other changes.

For every Configuration/Proxy bean discovered by Spring 6.x, three classpath lookups are triggered. Since these classes are not on the classpath, it causes a full classpath scan, ultimately resulting in a lookup failure (causing the delay).

One such example for the lookup for an application bean:

com.app.FooConfigBean$$SpringCGLIB$$0
com.app.FooConfigBean$$SpringCGLIB$$FastClass$$0
com.app.FooConfigBean$$SpringCGLIB$$FastClass$$1

Stack Trace for com.app.FooConfigBean$$SpringCGLIB$$0 lookup:

at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:534) at java.base/java.lang.Class.forName(Class.java:513) at org.springframework.cglib.core.ReflectUtils.loadClass(ReflectUtils.java:569) at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:351) at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:575) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator.java:107) at org.springframework.cglib.core.internal.LoadingCache.lambda$createEntry$1(LoadingCache.java:52) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:57) at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:130) at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:317) at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:562) at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:407) at org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(ConfigurationClassEnhancer.java:156) at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:113) at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:534) at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:311) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:363) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:153) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:789) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:607) 

Stack Trace for com.app.FooConfigBean$$SpringCGLIB$$FastClass$$0 lookup

at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:534) at java.base/java.lang.Class.forName(Class.java:513) at org.springframework.cglib.core.ReflectUtils.loadClass(ReflectUtils.java:569) at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:351) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator.java:107) at org.springframework.cglib.core.internal.LoadingCache.lambda$createEntry$1(LoadingCache.java:52) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:57) at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:130) at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:317) at org.springframework.cglib.reflect.FastClass$Generator.create(FastClass.java:70) at org.springframework.cglib.proxy.MethodProxy.helper(MethodProxy.java:148) at org.springframework.cglib.proxy.MethodProxy.init(MethodProxy.java:89) at org.springframework.cglib.proxy.MethodProxy.create(MethodProxy.java:63) at com.app.FooConfigBean$$SpringCGLIB$$0.CGLIB$STATICHOOK1(<generated>) at com.app.FooConfigBean$$SpringCGLIB$$0.<clinit>(<generated>) 

Given this behavior has changed with Spring 6.x, is there a way to bypass these class lookups that will never be found on the current classpath?

This situation is reminiscent of the BeanInfo/Customizer lookups, which also caused a similar regression that could be disabled.

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: backportedAn issue that has been backported to maintenance branchestype: regressionA bug that is also a regression

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions