DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How To Check for JSON Insecure Deserialization (JID) Attacks With Java
  • Dependency Injection in Spring
  • Build a REST API With Just 2 Classes in Java and Quarkus
  • Effective Java Collection Framework: Best Practices and Tips

Trending

  • Unlocking AI Coding Assistants Part 4: Generate Spring Boot Application
  • A Developer's Guide to Mastering Agentic AI: From Theory to Practice
  • Breaking Bottlenecks: Applying the Theory of Constraints to Software Development
  • Unlocking the Benefits of a Private API in AWS API Gateway
  1. DZone
  2. Coding
  3. Languages
  4. Java Class Loading: Performance Impact

Java Class Loading: Performance Impact

Learn more about class loading in Java and its performance impact.

By 
Ram Lakshmanan user avatar
Ram Lakshmanan
DZone Core CORE ·
Aug. 01, 22 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
7.5K Views

Join the DZone community and get the full member experience.

Join For Free

java.lang.ClassLoader#loadClass() API is used by 3rd party libraries, JDBC Drivers, frameworks, and application servers to load a java class into the memory. Application developers don’t use this API frequently. However when they use the APIs such as ‘java.lang.Class.forName()’ or ‘org.springframework.util.ClassUtils.forName()’, they internally call this ‘java.lang.ClassLoader#loadClass()’ API.

Frequent usage of this API amongst different threads at runtime can slow down your application performance. Sometimes it can even make the entire application unresponsive. In this post let’s understand this API a little bit more and its performance impact.

What Is the Purpose of ‘classloader.loadclass()’ API?

 Typically, if we want to instantiate a new object, we write the code like this:

Java
 
new io.ycrash.DummyObject();

However, you can use ClassLoader.loadClass() API and also instantiate the object. Here is how the code will look:

Java
 
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
Class<?> myClass = classLoader.loadClass("io.ycrash.DummyObject"); 
myClass.newInstance(); 

You can notice in line #2 ‘classLoader.loadClass()’ is invoked. This line will load the ‘io.ycrash.DummyObject’ class into memory. In line #3 ‘io.ycrash.DummyObject’ class is instantiated using the ‘newInstance()’ API. 

 This way of instantiating the object is like touching the nose with your hand, by going through the back of your neck. You might wonder why someone might do this. You can instantiate the object using ‘new’ only if you know the name of the class at the time of writing the code. In certain circumstances, you might know the name of the class only during run-time. For example, if you are writing frameworks (like Spring Framework, XML parser, …) you will know the class names to be instantiated only during runtime. You will not know what classes you will be instantiated at the time of writing the code. In such circumstances, you will have to end up using the ‘ClassLoader.loadClass()’ API.

Where ‘classloader.loadclass()’ Is Used?

 ‘ClassLoader.loadClass()’ is used in several popular 3rd party libraries, JDBC Drivers, frameworks & application servers. This section highlights a few popular frameworks where the ‘ClassLoader.loadClass()’ API is used. 

Apache Xalan

 When you use the Apache Xalan framework to serialize and deserialize XML, ‘ClassLoader.loadClass()’ API will be used. Below is the stack trace of a thread that is using ‘ClassLoader.loadClass()’ API from the Apache Xalan framework.

Shell
 
at java.lang.ClassLoader.loadClass(ClassLoader.java:404) 
- locked <0x6d497769> (a com.wm.app.b2b.server.ServerClassLoader) 
at com.wm.app.b2b.server.ServerClassLoader.loadClass(ServerClassLoader.java:1175) 
at com.wm.app.b2b.server.ServerClassLoader.loadClass(ServerClassLoader.java:1108) 
at org.apache.xml.serializer.ObjectFactory.findProviderClass(ObjectFactory.java:503) 
at org.apache.xml.serializer.SerializerFactory.getSerializer(SerializerFactory.java:129) 
at org.apache.xalan.transformer.TransformerIdentityImpl.createResultContentHandler(TransformerIdentityImpl.j
ava:260) 
at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:330) 
at org.springframework.ws.client.core.WebServiceTemplate$4.extractData(WebServiceTemplate.java:441) 
:
:

Google GUICE Framework

 When you use the Google GUICE framework, ‘ClassLoader.loadClass()’ API will be used. Below is the stack trace of a thread that is using ‘ClassLoader.loadClass()’ API from the Google GUICE framework.

Shell
 
at java.lang.Object.wait(Native Method) 
-  waiting on hudson.remoting.RemoteInvocationHandler$RPCRequest@1e408f0 
at hudson.remoting.Request.call(Request.java:127) 
at hudson.remoting.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:160) 
at $Proxy5.fetch2(Unknown Source) at hudson.remoting.RemoteClassLoader.findClass(RemoteClassLoader.java:122) 
at java.lang.ClassLoader.loadClass(ClassLoader.java:321) 
-  locked hudson.remoting.RemoteClassLoader@15c7850 
at java.lang.ClassLoader.loadClass(ClassLoader.java:266) 
at com.google.inject.internal.BindingProcessor.visit(BindingProcessor.java:69) 
at com.google.inject.internal.BindingProcessor.visit(BindingProcessor.java:43) 
at com.google.inject.internal.BindingImpl.acceptVisitor(BindingImpl.java:93) 
at com.google.inject.internal.AbstractProcessor.process(AbstractProcessor.java:56) 
at com.google.inject.internal.InjectorShell$Builder.build(InjectorShell.java:183) 
at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:104) 
-  locked com.google.inject.internal.InheritingState@1c915a5 
at com.google.inject.Guice.createInjector(Guice.java:94) 
at com.google.inject.Guice.createInjector(Guice.java:71) 
at com.google.inject.Guice.createInjector(Guice.java:61) 
:
:

Oracle JDBC Driver

If you use Oracle JDBC Driver, the ‘ClassLoader.loadClass()’ API will be used. Below is the stack trace of a thread that is using ‘ClassLoader.loadClass()’ API from the Oracle JDBC Driver.

Shell
 
at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:482) 
- waiting to lock <0xffffffff11a5f7d8> (a com.ibm.ws.classloader.CompoundClassLoader) 
at java.lang.ClassLoader.loadClass(ClassLoader.java:247) at java.lang.Class.forName0(Native Method) 
at java.lang.Class.forName(Class.java:170) 
at oracle.jdbc.driver.PhysicalConnection.safelyGetClassForName(PhysicalConnection.java:4682) 
at oracle.jdbc.driver.PhysicalConnection.addClassMapEntry(PhysicalConnection.java:2750) 
at oracle.jdbc.driver.PhysicalConnection.addDefaultClassMapEntriesTo(PhysicalConnection.java:2739) 
at oracle.jdbc.driver.PhysicalConnection.initializeClassMap(PhysicalConnection.java:2443) 
at oracle.jdbc.driver.PhysicalConnection.ensureClassMapExists(PhysicalConnection.java:2436) 
:
:

AspectJ library

If you use the AspectJ library, ‘ClassLoader.loadClass()’ API will be used. Below is the stack trace of a thread that is using ‘ClassLoader.loadClass()’ API from the AspectJ framework.

Shell
 
: 
:
at java.base@11.0.7/java.lang.ClassLoader.loadClass(ClassLoader.java:522) 
at java.base@11.0.7/java.lang.Class.forName0(Native Method) 
at java.base@11.0.7/java.lang.Class.forName(Class.java:398) 
at 
app//org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegateFactory.createDelegate(ReflectionBase
dReferenceTypeDelegateFactory.java:38) 
at app//org.aspectj.weaver.reflect.ReflectionWorld.resolveDelegate(ReflectionWorld.java:195) 
at app//org.aspectj.weaver.World.resolveToReferenceType(World.java:486) 
at app//org.aspectj.weaver.World.resolve(World.java:321) 
- locked java.lang.Object@1545fe7d 
at app//org.aspectj.weaver.World.resolve(World.java:231) 
at app//org.aspectj.weaver.World.resolve(World.java:436) 
at 
app//org.aspectj.weaver.internal.tools.PointcutExpressionImpl.couldMatchJoinPointsInType(PointcutExpressi
onImpl.java:83) 
at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:275) 
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:225) 
:
:

Studying Performance Impact 

 Now I assume you have got sufficient understanding of the Java class loading. Now it’s time to study its performance impact. To facilitate our study, I created this simple program:

Java
 
package io.ycrash.classloader;  
 public class MyApp extends Thread {     
   @Override 
   public void run() { 
      
       try { 
          
          while (true) { 
             
             ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
             Class<?> myClass = classLoader.loadClass("io.ycrash.DummyObject"); 
             myClass.newInstance(); 
          }      
       } catch (Exception e) { 
          
       } 
   } 
   
   public static void main(String args[]) throws Exception { 
       
       for (int counter = 0; counter < 10; ++counter) { 
          
          new MyApp().start(); 
       } 
   } 
}

If you notice this program, I am creating 10 threads in the main() method. 

Each thread goes on an infinite loop and instantiates ‘io.ycrash.DummyObject’ in the run() method, using the ‘classLoader.loadClass()’ API in line# 13. It means ‘classLoader.loadClass()’ going to be called repeatedly again and again by all these 10 threads.

Classloader.loadclass() – Blocked Threads

 We executed the above program. While the program was executing we ran the open source yCrash script. This script captures 360-degree data (thread dump, GC log, heap dump, netstat, VMstat, iostat, top, kernel logs,…) from the application. We analyzed the captured thread dump using fastThread – a thread dump analysis tool. The thread dump analysis report generated by this tool for this program can be found here. Tool reported that 9 threads out of 10 were in the BLOCKED state. If a thread is in the BLOCKED state, it indicates that it is stuck for a resource. When it’s in a BLOCKED state, it wouldn’t progress forward. It will hamper the application’s performance. You might wonder – Why does the above simple program make the threads enter into the BLOCKED state?
transitive graph showing 9 BLOCKED threads

Fig: transitive graph showing 9 BLOCKED threads (generated by fastThread)

 Above is the excerpt from the thread dump analysis report. You can see that 9 threads (‘Thread-0’, ‘Thread-1’, ‘Thread-2’, ‘Thread-3’, ‘Thread-4’, ‘Thread-5’, ‘Thread-7’, ‘Thread-8’, ‘Thread-9’) are BLOCKED by the ‘Thread-6’. Below is the stack trace of the one BLOCKED state thread (i.e. Thread-9):

Shell
 
Thread-9 
Stack Trace is: 
java.lang.Thread.State: BLOCKED (on object monitor) 
at java.lang.ClassLoader.loadClass(ClassLoader.java:404) 
- waiting to lock <0x00000003db200ae0> (a java.lang.Object) 
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335) 
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 
at io.ycrash.classloader.MyApp.run(MyApp.java:13) 
Locked ownable synchronizers: 
- None

You can notice that ‘Thread-9’ is BLOCKED on the java.lang.ClassLoader.loadClass() method. It’s waiting to acquire a lock on ‘<0x00000003db200ae0>’. All other remaining 8 threads which are in the BLOCKED state also have the exact same stack trace. 

 Below is the stack trace of ‘Thread-6’ who is blocking all other 9 threads:

Shell
 
Thread-6 
java.lang.Thread.State: RUNNABLE 
at java.lang.ClassLoader.findLoadedClass0(Native Method) 
at java.lang.ClassLoader.findLoadedClass(ClassLoader.java:1038) 
at java.lang.ClassLoader.loadClass(ClassLoader.java:406) 
- locked <0x00000003db200ae0> (a java.lang.Object) 
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335) 
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 
at io.ycrash.classloader.MyApp.run(MyApp.java:13) 
Locked ownable synchronizers: 
- None

You can notice that ‘Thread-6’ was able to acquire the lock (i.e. ‘<0x00000003db200ae0>’) and progress further. However, all other 9 threads are stuck waiting to acquire this lock.

Why Do Threads Become Blocked When Invoking Classloader.loadclass()?

 To understand why threads enter into the BLOCKED state when invoking ‘ClassLoader.loadClass()’ method, we will have to look at its source code. Below is the source code excerpt of ClassLoader.loadClass() method. If you would like to see the complete source code of java.lang.ClassLoader, you may refer to it here:

Java
 
 protected Class<?> loadClass(String name, boolean resolve)        
            throws ClassNotFoundException    
    {        
        synchronized (getClassLoadingLock(name)) {            
            // First, check if the class has already been loaded            
            Class<?> c = findLoadedClass(name);            
            if (c == null) {                
               long t0 = System.nanoTime();                
               try {                    
                   if (parent != null) {                        
                       c = parent.loadClass(name, false);                    
                   } else {                        
                       c = findBootstrapClassOrNull(name);                   
                   }                    
                   :                    
                   :  

In the highlighted line of the source code, you will see the usage of the ‘synchronized’ code block. When a block of code is synchronized, only one thread will be allowed to enter that block. In our above example 10 threads are trying to access ‘ClassLoader.loadClass()’ concurrently. Only one thread will be allowed to enter into the synchronized code block, the remaining 9 threads will be put into a BLOCKED state. 

Below is the source code of ‘getClassLoadingLock()’ method which returns an object and upon which synchronization happens.

Java
 
protected Object getClassLoadingLock(String className) {   
    Object lock = this;   
    if (parallelLockMap != null) {      
       Object newLock = new Object();      
       lock = parallelLockMap.putIfAbsent(className, newLock);      
       if (lock == null) {     
          lock = newLock;      
       }   
     }   
     return lock; 
}

You can notice that the ‘getClassLoadingLock()’ method will return the same object every time for the same class name. i.e. if the class name is ‘io.ycrash.DummyObject’ – it will return the same object every time. Thus all the 10 threads will be getting back the same object. And on this one single object, synchronization will happen. It will put all the threads into the BLOCKED state.

How To Fix This Problem?

This problem is stemming because ‘io.ycrash.DummyObject’ class is loaded again & again on every loop iteration. This causes the threads to enter into the BLOCKED state. This problem can be short-circuited if we can load the class only once during application startup time. This can be achieved by modifying the code as shown below.

Java
 
package io.ycrash.classloader; 
 
public class MyApp extends Thread { 
  
   private Class<?> myClass = initClass(); 
   
   private Class<?> initClass() { 
      
      try {         
         ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
         return classLoader.loadClass("io.ycrash.DummyObject"); 
      } catch (Exception e) {         
      }      
      
      return null; 
   } 
   
   @Override 
   public void run() { 
      
      while (true) { 
      
         try {            
            myClass.newInstance(); 
         } catch (Exception e) {         
         } 
      } 
   } 
   
   public static void main(String args[]) throws Exception { 
      
      for (int counter = 0; counter < 10; ++counter) { 
         
         new MyApp().start(); 
      } 
   } 
}

Making this code change resolved the issue. If you see now ‘myClass’ is initialized in line# 5. Unlike the earlier approach where myClass was initialized every single loop iteration, now myClass is initialized only once when the Thread is instantiated. Because of this shift in the code, the ‘ClassLoader.loadClass()’ API will not be called multiple times. Thus it will prevent threads from entering into the BLOCKED state.

Solutions

If your application also encounters this classloading performance problem, then here are the potential solutions to resolve it.

  1. Try to see whether you can invoke the ‘ClassLoader.loadClass()’ API during application startup time instead of run-time.
  2. If your application is loading the same class again & again at runtime, then try to load the class only once. After that point, cache the class and re-use it, as shown in the above example.
  3. Use the troubleshooting tools like fastThread, yCrash, … to detect which framework or 3rd party library, or code path is triggering the problem. Check whether frameworks have given any fixes in their latest version, if so upgrade to the latest version.
API application Framework Java (programming language) Object (computer science)

Published at DZone with permission of Ram Lakshmanan, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How To Check for JSON Insecure Deserialization (JID) Attacks With Java
  • Dependency Injection in Spring
  • Build a REST API With Just 2 Classes in Java and Quarkus
  • Effective Java Collection Framework: Best Practices and Tips

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!