You know how the Java class is loaded it?

I. Introduction

Recently gave a non-Java under the direction of a friend told parents delegation model, a friend asked me to write ClassLoader JVM at depth research articles, and indeed for a long time I did not write JVM related articles, a little hand itch, also painted Pi Yan Ping suppression do not live.

When I explained to the friend says so: parents delegate model, ClassLoader loaded class, will first referred to its parent ClassLoader loaded only when the case of parent ClassLoader load failed, will try to load their own . This allows the class multiplexing section, and the isolation portion may be implemented class, because different ClassLoader loaded classes are isolated from each other.

But others hastily explained to parents delegation model is wrong, if without knowing the JVM class loading mechanism, but also a good understanding of how "different ClassLoader loaded classes are isolated from each other," the Bible? So in order to understand the parents delegate, the best way is to first understand the loading process under the ClassLoader.

Two: Java class is how loaded

2.1: When loading the class
we must first know that, Java classes when they will be loaded?

"In-depth understanding of the Java Virtual Machine" The answer given is:

1: experiencing new, getstatic, putstatic and other instruction.
2: The class is called reflection time.
3: When initiating a subclass of class.
4: The main class loader will first set when the virtual machine starts.
5: Using dynamic language support JDK 1.7 times.
In fact, I'd say the most user-friendly answer is: When the time required during operation of this class.

Then we might start talking about how to load the class.

2.2: how to load a class
using the ClassLoader to load the class is very simple, direct call ClassLoder of loadClass () method can, I believe we will, but still have to give chestnuts:

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Test.class.getClassLoader().loadClass("com.wangxiandeng.test.Dog");
    }
}复制代码

The code above will be achieved so that ClassLoader to load "com.wangxiandeng.test.Dog" This class is not so easy. But the API JDK provided by the tip of the iceberg, it seems very simple call, in fact, hides a lot of details, my folks, like most to do is to uncover the package API, check it out.

2.3: JVM is how to load class
JVM default ClassLoader used to load user program is AppClassLoader, but no matter what ClassLoader, it's the root class are java.lang.ClassLoader. In the example above, loadClass () method eventually calls to ClassLoader.definClass1 (), which is a Native method.

static native Class<?> defineClass1(ClassLoader loader, String name, byte[] b, int off, int len,
                                        ProtectionDomain pd, String source); 复制代码

See Native methods Mo flustered, do not worry, OpenJDK open source, and so I continue to fly is!

definClass1 () corresponding to the JNI method Java_java_lang_ClassLoader_defineClass1 ()

JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass1(JNIEnv *env,
                                        jclass cls,
                                        jobject loader,
                                        jstring name,
                                        jbyteArray data,
                                        jint offset,
                                        jint length,
                                        jobject pd,
                                        jstring source)
{
    ......
    result = JVM_DefineClassWithSource(env, utfName, loader, body, length, pd, utfSource);
    ......
    return result;
}复制代码

Java_java_lang_ClassLoader_defineClass1 mainly called JVM_DefineClassWithSource () to load classes, go down along with the source code, you will find the final call is jvm.cpp in jvm_define_class_common () method.

static jclass jvm_define_class_common(JNIEnv *env, const char *name,
                                      jobject loader, const jbyte *buf,
                                      jsize len, jobject pd, const char *source,
                                      TRAPS) {
  ......
  ClassFileStream st((u1*)buf, len, source, ClassFileStream::verify);
  Handle class_loader (THREAD, JNIHandles::resolve(loader));
  if (UsePerfData) {
    is_lock_held_by_thread(class_loader,
                           ClassLoader::sync_JVMDefineClassLockFreeCounter(),
                           THREAD);
  }
  Handle protection_domain (THREAD, JNIHandles::resolve(pd));
  Klass* k = SystemDictionary::resolve_from_stream(class_name,
                                                   class_loader,
                                                   protection_domain,
                                                   &st,
                                                   CHECK_NULL);
  ......

  return (jclass) JNIHandles::make_local(env, k->java_mirror());
}复制代码

This logic is the use of the above main ClassFileStream to be loaded class files into the file stream, then call SystemDictionary :: resolve_from_stream (), generating a representative in the JVM Class: Klass. For Klass, they may not be familiar, but here have got off his. To put it plainly, it is used to define a data structure JVM Java Class of. Klass but only a base class, Java Class true data structures are defined in the InstanceKlass.

class InstanceKlass: public Klass {
 
 protected:
 
  Annotations*    _annotations;
  ......
  ConstantPool* _constants;
  ......
  Array<jushort>* _inner_classes;
  ......
  Array<Method*>* _methods;
  Array<Method*>* _default_methods;
  ......
  Array<u2>*      _fields;
}复制代码

Visible InstanceKlass recorded in all the properties of a Java class, including notes, methods, fields, inner classes, constant pool information. This information was originally recorded in the Class file, so that, InstanceKlass is a Java Class file is loaded into memory after the form.

Back to the top of the class loading process, here called SystemDictionary :: resolve_from_stream (), load the file into memory Class Klass.

resolve_from_stream () is the most important! The main logic has the following steps:

1: determining whether to allow parallel load classes, and lock according to the judgment result.

bool DoObjectLock = true;
if (is_parallelCapable(class_loader)) {
  DoObjectLock = false;
}
ClassLoaderData* loader_data = register_loader(class_loader, CHECK_NULL);
Handle lockObject = compute_loader_lock_object(class_loader, THREAD);
check_loader_lock_contention(lockObject, THREAD);
ObjectLocker ol(lockObject, THREAD, DoObjectLock);复制代码

If you allow parallel loading, ClassLoader will not be locked, only the lock SystemDictionary. Otherwise, it will use ObjectLocker to lock ClassLoader, ClassLoader ensure the same at the same time you can only load a class. ObjectLocker acquires a lock in its constructor and destructor releases the lock.

Allow the benefits of parallel loading of the lock granularity is fine, so you can load multiple files at the same time Class.

2: parse the file stream to generate InstanceKlass.

InstanceKlass* k = NULL;

k = KlassFactory::create_from_stream(st,
                                         class_name,
                                         loader_data,
                                         protection_domain,
                                         NULL, // host_klass
                                         NULL, // cp_patches
                                         CHECK_NULL);复制代码

3: Use Klass SystemDictionary registered generated.

SystemDictionary class information is used to help hold off the ClassLoader loaded. To be exact, SystemDictionary not a container, a container that holds true for class information is Dictionary, each ClassLoaderData are preserved in a private Dictionary, and SystemDictionary just a tool class has a lot of static methods only.

Let's look at registration code:

if (is_parallelCapable(class_loader)) {
  InstanceKlass* defined_k = find_or_define_instance_class(h_name, class_loader, k, THREAD);
  if (!HAS_PENDING_EXCEPTION && defined_k != k) {
    // If a parallel capable class loader already defined this class, register 'k' for cleanup.
    assert(defined_k != NULL, "Should have a klass if there's no exception");
    loader_data->add_to_deallocate_list(k);
    k = defined_k;
  }
} else {
  define_instance_class(k, THREAD);
}复制代码

If you allow parallel loading, the front would not have been ClassLoader lock, so at the same time, it may be loaded multiple times for the same Class file. But the same Class must be unique within the same ClassLoader, so there will first use the query whether SystemDictionary ClassLoader already mounted the same Class.

If you have already been loaded, it will be the current thread just loaded InstanceKlass to be added to the recovery list, and InstanceKlass * k redirected queries to use SystemDictionary InstanceKlass.
If you can not find any, then it will just loaded InstanceKlass registered in the Dictionary ClassLoader in.
Although the parallel load not lock ClassLoader, but will be locked at the time of registration of SystemDictionary InstanceKlass, there is no need to worry about InstanceKlass concurrent operations during registration.

If you disable the parallel load, then the direct use of SystemDictionary will InstanceKlass registered to the Dictionary ClassLoader can be.

resolve_from_stream () of the main process is three steps above, it is clear that the most important thing is the second step, the flow generation InstanceKlass from a file.

InstanceKlass call is generated KlassFactory :: create_from_stream () method, which is below the main logic code.

ClassFileParser parser(stream,
                       name,
                       loader_data,
                       protection_domain,
                       host_klass,
                       cp_patches,
                       ClassFileParser::BROADCAST, // publicity level
                       CHECK_NULL);

InstanceKlass* result = parser.create_instance_klass(old_stream != stream, CHECK_NULL);复制代码

The original ClassFileParser is the real hero ah! It is the sublimation Class files into InstanceKlass behind the big brother!

2.4: have to say ClassFileParser
ClassFileParser loading entrance is create_instance_klass Class file (). As the name suggests, it is used to create InstanceKlass.

create_instance_klass () would have done two major things:

(1): to allocate memory for InstanceKlass

InstanceKlass* const ik =
    InstanceKlass::allocate_instance_klass(*this, CHECK_NULL);复制代码

(2): Class Analysis file memory area is filled InstanceKlass

fill_instance_klass (ik, changed_by_loadhook, CHECK_NULL) ;
we said the first thing first for the Road, to allocate memory for InstanceKlass.

Memory allocation code is as follows:

const int size = InstanceKlass::size(parser.vtable_size(),
                                       parser.itable_size(),
                                       nonstatic_oop_map_size(parser.total_oop_map_count()),
                                       parser.is_interface(),
                                       parser.is_anonymous(),
                                       should_store_fingerprint(parser.is_anonymous()));
ClassLoaderData* loader_data = parser.loader_data();
InstanceKlass* ik;
ik = new (loader_data, size, THREAD) InstanceKlass(parser, InstanceKlass::_misc_kind_other);复制代码

First calculate the size InstanceKlass here in memory of, you know, this size is determined in Class files compiled after.

Then it new a new InstanceKlass object. This is not a simple memory allocated on the heap, it should be noted that Klass for new operator is overloaded:

void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {
  return Metaspace::allocate(loader_data, word_size, MetaspaceObj::ClassType, THREAD);
}复制代码

When the call distribution InstanceKlass Metaspace :: allocate ():

                              MetaspaceObj::Type type, TRAPS) {
  ......
  MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;
  ......
  MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype);
  ......
  return result;
}复制代码

Thus, InstanceKlass method area is partitioned Metaspace (element space) of ClassLoader. JDK8 from the beginning, HotSpot, there is no permanent generations, classes are assigned Metaspace in. Metaspace and permanent generations is not the same, using the Native Memory, permanent behalf Limited by MaxPermSize, when the memory is not enough to run out of memory.

After InstanceKlass memory allocation, we will have to start the second thing, Class document analysis, filling InstanceKlass memory area.

ClassFileParser when construction will start analyzing the Class file, so fill_instance_klass () only needs to fill. After filling, it will call java_lang_Class :: create_mirror () to create InstanceKlass Class objects in the Java layer.

void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loadhook, TRAPS) {
  .....
  ik->set_class_loader_data(_loader_data);
  ik->set_nonstatic_field_size(_field_info->nonstatic_field_size);
  ik->set_has_nonstatic_fields(_field_info->has_nonstatic_fields);
  ik->set_static_oop_field_count(_fac->count[STATIC_OOP]);
  ik->set_name(_class_name);
  ......

  java_lang_Class::create_mirror(ik,
                                 Handle(THREAD, _loader_data->class_loader()),
                                 module_handle,
                                 _protection_domain,
                                 CHECK);
}复制代码

Incidentally, for the Class file structure is not familiar with the students, can look two years ago, I wrote an article:

"Mr. Wang: Jvm only parse java class files"

Here, Class document has completed a magnificent turn, by the cold binary file, into a memory full of vitality InstanceKlass.

Three: Parents talk to delegate

If you patiently read the source code analysis above, you must be "different ClassLoader loaded classes are separated from each other", that is understanding and on a higher level.

We summarize: Each ClassLoader has a Dictionary to save InstanceKlass information it loaded. And, each ClassLoader through locks to ensure that for the same Class, it will only register a InstanceKlass to his Dictionary.

Official For these reasons above, if all the ClassLoader to load Class by their own files, it will lead to the same Class file, there are multiple copies InstanceKlass, so even with a Class file, derived from different InstanceKlasss instance type is not the same of.

For chestnuts, we customize a ClassLoader, used to break parent delegation model:

public class CustomClassloader extends URLClassLoader {

    public CustomClassloader(URL[] urls) {
        super(urls);
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (name.startsWith("com.wangxiandeng")) {
            return findClass(name);
        }
        return super.loadClass(name, resolve);
    }
}复制代码

Studen then try to load the class, and instantiate:

public class Test {

    public static void main(String[] args) throws Exception {
        URL url[] = new URL[1];
        url[0] = Thread.currentThread().getContextClassLoader().getResource("");

        CustomClassloader customClassloader = new CustomClassloader(url);
        Class clazz = customClassloader.loadClass("com.wangxiandeng.Student");

        Student student = (Student) clazz.newInstance();
    }
}复制代码

After running strong turn will throw an exception type:

Exception in thread "main" java.lang.ClassCastException:
      com.wangxiandeng.Student cannot be cast to com.wangxiandeng.Student复制代码

why?

Because InstanceKlass Student object instantiated by the loading CustomClassLoader belongs is generated, and we turn stronger type Student.Class corresponding InstanceKlass by the system default ClassLoader generated, they are two unrelated InstanceKlass nature, Of course, not strong turn.

Some students asked: Why "strong turn type Student.Class corresponding InstanceKlass by the system default ClassLoader generated"?
In fact, very simple, we decompile the next byte code:

  public static void main(java.lang.String[]) throws java.lang.Exception;
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=5, args_size=1
         0: iconst_1
         1: anewarray     #2                  // class java/net/URL
         4: astore_1
         5: aload_1
         6: iconst_0
         7: invokestatic  #3                  // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        10: invokevirtual #4                  // Method java/lang/Thread.getContextClassLoader:()Ljava/lang/ClassLoader;
        13: ldc           #5                  // String
        15: invokevirtual #6                  // Method java/lang/ClassLoader.getResource:(Ljava/lang/String;)Ljava/net/URL;
        18: aastore
        19: new           #7                  // class com/wangxiandeng/classloader/CustomClassloader
        22: dup
        23: aload_1
        24: invokespecial #8                  // Method com/wangxiandeng/classloader/CustomClassloader."<init>":([Ljava/net/URL;)V
        27: astore_2
        28: aload_2
        29: ldc           #9                  // String com.wangxiandeng.Student
        31: invokevirtual #10                 // Method com/wangxiandeng/classloader/CustomClassloader.loadClass:(Ljava/lang/String;)Ljava/lang/Class;
        34: astore_3
        35: aload_3
        36: invokevirtual #11                 // Method java/lang/Class.newInstance:()Ljava/lang/Object;
        39: checkcast     #12                 // class com/wangxiandeng/Student
        42: astore        4
        44: return复制代码

After the initialization can be seen using the example of Class loading, the type conversion call checkcast, after checkcast index # 12 is the Student operand in the constant pool of the class:

# 12 = Class # 52 // com / wangxiandeng / Student
Now we can look at the implementation checkcast in the HotSpot.
HotSpot is available in three bytecode execution engine is currently used in the template interpreter, I can look at this article: "Mr. Wang: JVM's template interpreter."
Early it uses HotSpot bytecode interpreter. Template interpreter for execution of the instructions are written in assembler, and translated bytecode interpreter using the C ++ carried out, in order to look more comfortable, we will not see a compilation, direct look at the bytecode interpreter on the line. If your assembler skills well, of course, you can also see the direct template interpreter, before I wrote the article "Mr. Wang: JVM create objects of source code analysis," here is the analysis of the template interpreter achieved for new instructions.

Ado, let's look at the bytecode interpreter for achieving checkcast, the code in the bytecodeInterpreter.cpp

CASE(_checkcast):
    if (STACK_OBJECT(-1) != NULL) {
      VERIFY_OOP(STACK_OBJECT(-1));
      // 拿到 checkcast 指令后的操作数,本例子中即 Student.Class 在常量池中的索引:#12
      u2 index = Bytes::get_Java_u2(pc+1);
      
      // 如果常量池还没有解析,先进行解析,即将常量池中的符号引用替换成直接引用,
      //此时就会触发Student.Class 的加载
      if (METHOD->constants()->tag_at(index).is_unresolved_klass()) {
        CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD), handle_exception);
      }
      // 获取上一步系统加载的Student.Class 对应的 InstanceKlass
      Klass* klassOf = (Klass*) METHOD->constants()->resolved_klass_at(index);
      // 获取要强转的对象的实际类型,即我们自己手动加载的Student.Class 对应的 InstanceKlass
      Klass* objKlass = STACK_OBJECT(-1)->klass(); // ebx
     
      // 现在就比较简单了,直接看看上面的两个InstanceKlass指针内容是否相同
      // 不同的情况下则判断是否存在继承关系
      if (objKlass != klassOf && !objKlass->is_subtype_of(klassOf)) {
        // Decrement counter at checkcast.
        BI_PROFILE_SUBTYPECHECK_FAILED(objKlass);
        ResourceMark rm(THREAD);
        char* message = SharedRuntime::generate_class_cast_message(
          objKlass, klassOf);
        VM_JAVA_ERROR(vmSymbols::java_lang_ClassCastException(), message, note_classCheck_trap);
      }
      // Profile checkcast with null_seen and receiver.
      BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/false, objKlass);
    } else {
      // Profile checkcast with null_seen and receiver.
      BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/true, NULL);
    }复制代码

Through the analysis of the above code, I believe we have understood the "strong turn type Student.Class corresponding InstanceKlass by the system default ClassLoader generated" this sentence.

Delegate benefits of parents is to try to ensure that the same file will generate a Class InstanceKlass, but in some cases, we have to go for broke delegated parents, for example, we want to achieve Class isolation time.

Reply to a question at street flute students:

// If constant pool has not been resolved, the first parsing constant pool is about to replace direct reference to symbolic reference,
// this time will trigger Student.Class load
if (METHOD-> constants () - > tag_at (index ) .is_unresolved_klass ()) {
CALL_VM (InterpreterRuntime :: quicken_io_cc (THREAD), handle_exception);
}
I ask, why here will reload Student.Class? jvm is not there own class loaded link, then follow the link to find out whether the system has been loaded class? Then how to customize CustomClassloader added to the query link go?

The first method: Start setting parameters java -Djava.system.class.loader

The second method: use Thread.setContextClassLoder

Here it is a bit tricky, and look Code:

public class Test {

    public static void main(String[] args) throws Exception {
        URL url[] = new URL[1];
        url[0] = Thread.currentThread().getContextClassLoader().getResource("");
        final CustomClassloader customClassloader = new CustomClassloader(url);
        Thread.currentThread().setContextClassLoader(customClassloader);
        Class clazz = customClassloader.loadClass("com.wangxiandeng.ClassTest");
        Object object = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("test");
        method.invoke(object);
    }
}
public class ClassTest {

    public void test() throws Exception{
        Class clazz = Thread.currentThread().getContextClassLoader().loadClass("com.wangxiandeng.Student");
        Student student = (Student) clazz.newInstance();
        System.out.print(student.getClass().getClassLoader());

    }
}复制代码

Note that after the ClassLoader set of threads, not directly call the new ClassTest (). Test (). why? Because direct strong references, then, will Test.Class when parsing constant pool, use the system default ClassLoader loaded ClassTest, which in turn triggered the analytical ClassTest.Class. To prevent this from happening, to load ClassTest.Class CustomClassLoader utilized herein, recycling reflection Test call (), the case when parsing ClassTest.Class constant pool, will use to load Class CustomClassLoader constant pool entry, and It will not be an exception occurred.

Four: summary

Finish this article, the hand does not itch, very cool! This article from the parents delegate spoke of load Class files, and finally back to the parents about the delegation, seems a bit around, in fact, only the loading mechanism Class of understanding in order to better understand the parents delegate such a mechanism similar to, or just rote by rote some of the empty theory, can not play inside out understanding.


Author: Middleware brother

Description link

This article Yunqi community original content may not be reproduced without permission.


Guess you like

Origin juejin.im/post/5d47eb8de51d4561ad654866