jvm(2)---类加载机制
1.类加载:类加载器将class文件加载到虚拟机的内存
加载:在硬盘上查找并通过IO读入字节码文件
连接:执行校验、准备、解析步骤
校验:校验字节码文件的正确性
准备:给类的静态变量分配内存,并赋予默认值
解析:类装载器装入类所引用的其他所有类
初始化:对类的静态变量初始化为指定的值,执行静态代码块
2.类加载器:
启动类加载器(BootstrapClassLoader):负责加载JRE的核心类库,如jre目标下的rt.jar,charsets.jar等
扩展类加载器(ExtensionClassLoader):负责加载JRE扩展目录ext中JAR类包
系统类加载器(ApplicationClassLoader):负责加载ClassPath路径下的类包
用户自定义加载器(CustomClassLoader):负责加载用户自定义路径下的类包
执行如下代码,查看对应的类加载器:
package com.nijunyang.spring; import com.sun.crypto.provider.BlowfishCipher; /** * @author: create by nijunyang * @date:2019/6/16 */ public class ClassLoaderTest { public static void main(String[] args) { System.out.println(String.class.getClassLoader()); System.out.println(BlowfishCipher.class.getClassLoader().getClass().getName()); System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName()); System.out.println(ClassLoader.getSystemClassLoader().getClass().getName()); } }
可以看到String的类加载器null,因为String是在java核心包rt.jar里面这里面东西是由启动类加载器加载的,而启动类加载器是由C/C++实现的,根本不在JDK里面
BlowfishCipher的类加载器是ExtClassLoader,而这个类正是Jre扩展目录ext中jar包所在的类
3.类加载机制--全盘委托和双亲委派机制
全盘委托:当一个ClassLoader加载一个类时,除非显示的使用另一个ClassLoader,该类所依赖和引用的类也由这个ClassLoader载入,比如说A依赖B,那么B的加载器和A的加载器一样。
双亲委派:指先委托父类加载器寻找目标类,在找不到的情况下在自己的路径中查找并载入目标类,感觉说父类加载器不是很合适,因为没有这一层关系,感觉更像是上下级关系。委托上级加载器寻找目标类。
双亲委派的好处:
1.避免类重复加载,上级加载了,下级就不需要再加载
2.因为核心API都有固定的加载器,可以防止核心库被篡改: 自定义一个String类,同样在java.lang包下,运行代码会发现启动报错,实际加载的java.lang.String类并没有main方法,说明实际加载的String类并不是我们自定义的String类,而是因为双亲委派机制,往上委托到启动类加载器,去加载rt.jar里面的String类
4.类加载过程
实际上jvm对class文件是按需加载的,需要的时候才加载(运行时动态加载),并非一次全部加载进去,jvm启动参数加上:-verbose:class 查看类加载
package com.nijunyang.spring; /** * @author: create by nijunyang * @date:2019/6/16 */ public class ClassLoaderTest { static { System.out.println("静态块执行"); } public static void main(String[] args) throws InterruptedException { Thread.sleep(3000); System.out.println(System.currentTimeMillis()); new A(); System.out.println("A对象创建完毕"); new B(); } }
从结果中我们可以看到
1.从本地/E:/IdeaProject/tuling/spring/target/classes/下面去加载当前运行类ClassLoaderTest.class文件
2.初始化类,执行静态代码块
3.main方法运行,睡了5s之后打印当前时间,当我们new A的时候才去本地加载A.class文件到内存,再实例化A
4.A创建完毕之后,再去加载B.class,完成B的初始化