石家庄网站seo外包,网站排名logo怎么做,成都市建设网站首页,关键词优化公司推荐当我们用java命令运行某个类的main函数启动程序时#xff0c;首先需要通过类加载器把类加载到JVM#xff0c;本文主要说明类加载机制和其具体实现双亲委派模式。
一、类加载机制
类加载过程#xff1a;
类加载的过程是将类的字节码加载到内存中的过程#xff0c;主要包括…当我们用java命令运行某个类的main函数启动程序时首先需要通过类加载器把类加载到JVM本文主要说明类加载机制和其具体实现双亲委派模式。
一、类加载机制
类加载过程
类加载的过程是将类的字节码加载到内存中的过程主要包括加载--链接--初始化其中链接还包括验证、准备、解析3个步骤。
加载将class文件加载到内存在方法区生成运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应Class实例的引用生成的Class实例对象放到堆中验证验证这个class文件是否合法包括文件格式的校验元数据类型的校验等准备为类变量分配内存空间但此时只是初始化为默认值而非真实值但对于final变量此时会初始化为真实值解析将符号引用相对引用转换为直接引用符号引用是class文件的相对表达方式直接引用就是在该系统里地址指针比如hello()方法为符号引用0x12345678为直接引用初始化初始化类变量成真实值初始化静态代码段实际执行的是类构造器 方法。 类加载过程是懒加载的策略只有当该类被使用了才会被初始化实际就是调用classLoader的 方法执行的过程会触发类的初始化操作条件为1需要创建新的对象执行了new操作2调用了类的静态变量或静态方法3通过反射机制来获取某个类的时候
利用new实例化对象的过程
Java的对象实例化的过程是调用 方法在进行new操作的时候会执行实例化操作实例化的过程主要 是调用构造方法的过程。在进行对象实例化前已经初始化静态变量和静态代码段实例化过程会初始化变量和代码块、调用构造方法进行实例化利用new操作实例化对象的过程
二、双亲委派
类加载过程主要是通过类加载器来实现的Java里有如下几种类加载器
引导类加载器Bootstrap ClassLoader负责加载支撑JVM运行的位于JRE的lib目录下的核心类库比如rt.jar、charsets.jar等扩展类加载器Extension ClassLoader负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包应用程序类加载器Application ClassLoader负责加载ClassPath路径下的类包主要就是加载你自己写的那些类自定义加载器Customer ClassLoader负责加载用户自定义路径下的类包 以下代码打印出各个类关联的ClassLoader情况
public class TestJDKClassLoader {public static void main(String[] args) {System.out.println(String.class.getClassLoader());System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName());System.out.println();ClassLoader appClassLoader ClassLoader.getSystemClassLoader();ClassLoader extClassloader appClassLoader.getParent();ClassLoader bootstrapLoader extClassloader.getParent();System.out.println(the bootstrapLoader : bootstrapLoader);System.out.println(the extClassloader : extClassloader);System.out.println(the appClassLoader : appClassLoader);System.out.println();System.out.println(bootstrapLoader加载以下文件);URL[] urls Launcher.getBootstrapClassPath().getURLs();for (int i 0; i urls.length; i) {System.out.println(urls[i]);}System.out.println();System.out.println(extClassloader加载以下文件);System.out.println(System.getProperty(java.ext.dirs));System.out.println();System.out.println(appClassLoader加载以下文件);System.out.println(System.getProperty(java.class.path));}
}
类加载的具体实现是通过双亲委派机制加载某个类时会先委托父加载器寻找目标类找不到再委托上层父加载器加载如果所有父加载器在自己的加载类路径下都找不到目标类则在自己的类加载路径中查找并载入目标类。
简单理解就是能父加载器做的事就父加载器做父加载器做不了的事情才自己来做。
设计双亲委派机制的好处是
保障类的唯一性ClassLoader的双亲委派模型保障一个类在类加载器的唯一性父类已经加载了该类子类就不再加载保障被加载类的唯一性。实现沙箱安全机制自己写的java.lang.String.class类不会被加载这样便可以防止核心API库被随意篡改。
类加载器实现原理
先说明类加载器本身初始化的逻辑
在sun.misc.Launcher类中创建JVM启动器实例。在Launcher构造方法内部其创建了两个类加载器分别是sun.misc.Launcher.ExtClassLoader(扩展类加载器)和sun.misc.Launcher.AppClassLoader(应用类加载器)。JVM默认使用Launcher的getClassLoader()方法返回的类加载器AppClassLoader的实例加载我们的应用程序。
//Launcher的构造方法public Launcher() {Launcher.ExtClassLoader var1;try {// 构造扩展类加载器在构造的过程中将其父加载器设置为nullvar1 Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError(Could not create extension class loader, var10);}try {//构造应用类加载器在构造的过程中将其父加载器设置为ExtClassLoader//Launcher的loader属性值是AppClassLoader我们一般都是用这个类加载器来加载我们自己写的应用程序this.loader Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError(Could not create application class loader, var9);}//设置当前线程的类加载器为应用类加载器Thread.currentThread().setContextClassLoader(this.loader);String var2 System.getProperty(java.security.manager);//...
}
再说明一下AppClassLoader的loadClass方法该方法是进行class文件加载的方法但最终会调用其父类ClassLoader的loadClass方法该方法的大体逻辑如下
首先检查一下指定名称的类是否已经加载过如果加载过了就不需要再加载直接返回。如果此类没有加载过那么再判断一下是否有父加载器如果有父加载器则由父加载器加载即调用parent.loadClass(name, false);.或者是调用bootstrap类加载器来加载。如果父加载器及bootstrap类加载器都没有找到指定的类那么调用当前类加载器的findClass方法来完成类加载。
//ClassLoader的loadClass方法里面实现了双亲委派机制
protected Class? loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// 检查当前类加载器是否已经加载了该类Class? c findLoadedClass(name);if (c null) {long t0 System.nanoTime();try {if (parent ! null) { //如果当前加载器父加载器不为空则委托父加载器加载该类c parent.loadClass(name, false);} else { //如果当前加载器父加载器为空则委托引导类加载器加载该类c findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c null) {// If still not found, then invoke findClass in order// to find the class.long t1 System.nanoTime();//都会调用URLClassLoader的findClass方法在加载器的类路径里查找并加载该类c findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) { //不会执行resolveClass(c);}return c;}
}
用户可以设置自定义类加载器来打破默认的双亲委派模式主要是实现loadClass()方法在这个方法中可以自定义加载class文件的逻辑。
public class MyClassLoaderTest {static class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath classPath;}private byte[] loadByte(String name) throws Exception {name name.replaceAll(\\., /);FileInputStream fis new FileInputStream(classPath / name .class);int len fis.available();byte[] data new byte[len];fis.read(data);fis.close();return data;}protected Class? findClass(String name) throws ClassNotFoundException {try {byte[] data loadByte(name);return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}/*** 重写类加载方法实现自己的加载逻辑不委派给双亲加载* param name* param resolve* return* throws ClassNotFoundException*/protected Class? loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass? c findLoadedClass(name);if (c null) {// If still not found, then invoke findClass in order// to find the class.long t1 System.nanoTime();c findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}if (resolve) {resolveClass(c);}return c;}}}public static void main(String args[]) throws Exception {MyClassLoader classLoader new MyClassLoader(xxx);//尝试用自己改写类加载机制去加载自己写的java.lang.String.classClass clazz classLoader.loadClass(java.lang.String);Object obj clazz.newInstance();Method method clazz.getDeclaredMethod(sout, null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}
打破双亲委托的方法
通过线程上下文类加载器Thread Context ClassLoader#setContextClassLoader(ClassLoader c)可以指定当前线程的类加载器如果创建线程时还未设置它将会从父线程中继承一个如果在应用程序的全局范围内都没有设置过那么这个类加载器默认就是应用程序类加载器。 本文由博客一文多发平台 OpenWrite 发布