Talking about Java class loader in combination with JVM source code

I. Introduction

The previous article Java class loader reveals the principle of Java class loader from the Java level. Here we will explain it in a little bit in-depth with the JVM source code.

Second, the delegation mechanism of Java class loader

The Java class loader uses a delegation mechanism, that is, when a class loader loads a class, it will first try to have the parent class loader load it. So the question is, why use this method?

First, use delegation to avoid repeated loading. Second, considering security factors, let's take a look at the loadClass method of the ClassLoader class:

protected Class<?> loadClass(Stringname,boolean resolve)  
       throws ClassNotFoundException  
   {  
       synchronized (getClassLoadingLock(name)) {  
           // 首先从jvm缓存查找该类
           Class c = findLoadedClass(name); // (1)
           if (c ==null) {  
               longt0 = System.nanoTime();  
               try {  //然后委托给父类加载器进行加载
                   if (parent !=null) {  
                       c = parent.loadClass(name,false);  (2)
                   } else {  //如果父类加载器为null,则委托给BootStrap加载器加载
                       c = findBootstrapClassOrNull(name);  (3)
                   }  
               } catch (ClassNotFoundExceptione) {  
                   // ClassNotFoundException thrown if class not found  
                   // from the non-null parent class loader  
               }  

               if (c ==null) {  
                   // 若仍然没有找到则调用findclass查找
                   // to find the class.  
                   longt1 = System.nanoTime();  
                   c = findClass(name);  (4)

                   // this is the defining class loader; record the stats  
                   sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 -t0);  
                   sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);  
                   sun.misc.PerfCounter.getFindClasses().increment();  
               }  
           }  
           if (resolve) {  
               resolveClass(c);  //(5)
           }  
           returnc;  
       }  
   }  

Code (1) means to look up the class from the JVM cache, and if the class has been loaded before, return the class directly from the JVM cache.

Code (2) means that if the class does not exist in the JVM cache, check whether the current class loader has a parent loader, and if so, entrust the parent class loader to load, otherwise call (3), entrust BootStrapClassloader to load, If it is still not found, call the findclass method of the current Classloader to find it.

Code (4) is to search from the specified path of the local classloader, in which the findClass method finds the Class file in the path and loads the binary bytecode into the memory, and then calls the native method defineClass1 to parse the bytecode into the kclass object inside the JVM, and then Stored in the method area of ​​the Java heap.

Code (5) is to perform link operation after bytecode is loaded into memory, verify file format and bytecode, allocate space for static field and initialize, convert symbolic reference to direct reference, access control, method coverage, etc., This article does not delve into these in depth.

3. How the defineClass1 of the JVM source code parses the bytecode file

The source code of openjdk7 used in this section, the definition of defineClass1 in the JVM source code is in the ClassLoader.c file, and the analysis sequence diagram is as follows:
image.png

It can be seen that step (8) specifically parses the bytecode file, and step (17) adds the loaded class to the system dictionary Map,

void SystemDictionary::update_dictionary(int d_index, unsigned int d_hash,
                                         int p_index, unsigned int p_hash,
                                         instanceKlassHandle k,
                                         Handle class_loader,
                                         TRAPS) {
  // Compile_lock prevents systemDictionary updates during compilations
  assert_locked_or_safepoint(Compile_lock);
  Symbol*  name  = k->name();
  ClassLoaderData *loader_data = class_loader_data(class_loader);

  {
  MutexLocker mu1(SystemDictionary_lock, THREAD);

  ...
  // 当前对象已经存在了?
  Klass* sd_check = find_class(d_index, d_hash, name, loader_data);
  //不存在则添加
  if (sd_check == NULL) {
      //添加kclass到系统词典
    dictionary()->add_klass(name, loader_data, k);
    notice_modification();
  }
  ...
}

The key uses the package path + class name of the class, the class loader is determined by both, and the value is the instanceKlassHandle object corresponding to the specific loaded class, which maintains the kclass object. That is, a class is uniquely identified in the system dictionary using the class loader and the class's package path class name. This also verifies that after the same class is loaded using two class loaders in Java, the two loaded classes are different and cannot be assigned to each other.

Fourth, how to findLoadedClass0 of JVM source code to find out whether a class has been loaded

findLoadedClass0 is also in the ClassLoader.c file, and its search sequence diagram: The
image.png
above sequence diagram mainly depends on the find method of SystemDictionary:

Klass* SystemDictionary::find(Symbol* class_name,
                              Handle class_loader,
                              Handle protection_domain,
                              TRAPS) {

  ...
  class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
  ClassLoaderData* loader_data = ClassLoaderData::class_loader_data_or_null(class_loader());

  ...
  unsigned int d_hash = dictionary()->compute_hash(class_name, loader_data);
  int d_index = dictionary()->hash_to_index(d_hash);

  {
    ... 
    return dictionary()->find(d_index, d_hash, class_name, loader_data,
                              protection_domain, THREAD);
  }
}


Klass* Dictionary::find(int index, unsigned int hash, Symbol* name,
                          ClassLoaderData* loader_data, Handle protection_domain, TRAPS) {
  //根据类名和加载器计算对应的kclass在map里面对应的key
  DictionaryEntry* entry = get_entry(index, hash, name, loader_data);
  //存在,并且验证通过则返回
  if (entry != NULL && entry->is_valid_protection_domain(protection_domain)) {
    return entry->klass();
  } else {
     //否者返回null,说明不存在
    return NULL;
  }
}

It can be seen that after finding whether a class has been loaded, it is also found from the system dictionary according to the class name and class loader.

V. Summary

From the perspective of JVM source code, this paper analyzes the only class name in Java that contains the package path and the class loader uniquely determines a class. In the global system dictionary, the key corresponding to the loaded class is calculated according to the class name of the package path and the class loader.

Finally, the high-performance RPC framework Dubbo has been released from entry to in-depth, welcome to discuss ^^

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325861247&siteId=291194637