从JDK源码级别剖析JVM类加载机制
类加载运行的全过程
当用java命令运行某个main函数时,首先需要类加载器把主类加载到JVM内存中。
通过Java命令执行代码的大致流程为
将编译好的字节码class文件通过java命令,在win操作系统就是一个java.exe文件,这个文件底层是c++语言实现的,通过这个文件调用底层jvm.dll文件创建Java虚拟机,这个jvm.dll文件也是c++语言实现的就是一些类库。在创建JVM虚拟机的过程中会通过c++语言创建一个引导类加载器。最终由C++语言调用java代码创建JVM启动器Launcher,该类由引导类加载器创建其他类加载器,最终调用的Launcher类的getLauncher()方法,这个方法创建加载了扩展类加载器,应用程序类加载器。最终调用loadClass()方法实现类的加载,这个方法体现了双亲委派机制。
java里面有一下几种类加载器:
引导类加载器:加载jre/lib下核心jar包 如rt.jar charset.jar
扩展类加载器:加载jre/lib/ext下面的jar包
应用程序类加载器:加载自己写的java类
自定义类加载器:负责加载用户自定义路径下的类包
如果想要摆脱双亲委派机制可以重写loadclass方法
//ClassLoader的loadClass方法,里面实现了双亲委派机制 2 protected Class<?> loadClass(String name, boolean resolve) 3 throws ClassNotFoundException 4 { 5 synchronized (getClassLoadingLock(name)) { 6 // 检查当前类加载器是否已经加载了该类 7 Class<?> c = findLoadedClass(name); 8 if (c == null) { 9 long t0 = System.nanoTime(); 10 try { 11 if (parent != null) { //如果当前加载器父加载器不为空则委托父加载器加载该类 12 c = parent.loadClass(name, false); 13 } else { //如果当前加载器父加载器为空则委托引导类加载器加载该类 14 c = findBootstrapClassOrNull(name); 15 } 16 } catch (ClassNotFoundException e) { 17 // ClassNotFoundException thrown if class not found 18 // from the non‐null parent class loader 19 } 2021 if (c == null) { 22 // If still not found, then invoke findClass in order 23 // to find the class. 24 long t1 = System.nanoTime(); 25 //都会调用URLClassLoader的findClass方法在加载器的类路径里查找并加载该类 26 c = findClass(name); 27 28 // this is the defining class loader; record the stats 29 sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 ‐ t0); 30 sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); 31 sun.misc.PerfCounter.getFindClasses().increment(); 32 } 33 } 34 if (resolve) { //不会执行 35 resolveClass(c); 36 } 37 return c; 38 } 39 }
如果想自己定义类加载器,定义要加载自己的类路径则重新编写findClass()方法;
为什么jdk设计者要对类加载实现双亲委派机制?
1.沙箱安全机制 自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改
2.避免类的重复加载 父类加载过得类,子类没必要重复加载