2

Java的类加载性能问题及解决办法 - DZone

 2 years ago
source link: https://www.jdon.com/61774
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java的类加载性能问题及解决办法 - DZone
Java在调用 Classloader.loadclass() 时线程会被阻塞,看它的源代码。下面是 ClassLoader.loadClass() 方法的源代码摘录。如果您想查看完整的源代码java.lang.ClassLoader,可以参考这里

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);                   
                   }                    
                   :                    
                   :  

在源代码的行中,你会看到 "

synchronized

同步 "代码块的用法。当一个代码块被同步时,只有一个线程被允许进入该代码块。在我们上面的例子中,有10个线程试图同时访问 "ClassLoader.loadClass()"。只有一个线程被允许进入同步代码块,其余9个线程将被置入阻塞状态。

下面是'getClassLoadingLock()'方法的源代码,该方法返回一个对象,在此基础上发生同步:

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; 
}

注意到'getClassLoadingLock()'方法对于相同的类名每次都会返回相同的对象,即如果类名是'io.ycrash.DummyObject' - 它每次都会返回相同的对象。因此,所有的10个线程都会得到相同的对象。而在这个单一的对象上,将发生同步化。它将使所有的线程进入BLOCKED状态。

如何解决这个问题?
这个问题是由于'io.ycrash.DummyObject'类在每个循环迭代中被反复加载。这导致线程进入阻断状态。如果我们能在应用程序启动时只加载一次该类,那么这个问题就可以得到解决。这可以通过修改下面的代码来实现。

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(); 
      } 
   } 
}

如果你看到现在'myClass'只一次被初始化了。不像之前的方法,每次循环迭代都要初始化myClass,现在myClass只在线程实例化时被初始化一次。
由于代码中的这一转变,"ClassLoader.loadClass() "API将不会被多次调用。因此,它将防止线程进入BLOCKED状态。

总结
如果你的应用程序也遇到了这个类加载性能问题,那么以下是解决这个问题的潜在方案:

  • 尝试看看你是否可以在应用程序启动时而不是运行时调用 "ClassLoader.loadClass() "API。
  • 如果你的应用程序在运行时反复加载同一个类,那么试着只加载一次该类。在那之后,缓存该类并重新使用它,如上例所示。
  • 使用故障排除工具,如fastThread、yCrash......来检测哪个框架或第三方库,或代码路径触发了这个问题。检查框架是否在其最新版本中给出了任何修复,如果是,就升级到最新版本。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK