JVM创建加载流程
ps:1.图中c++的jvm.dll就类似java中的jar包
2. 上图中的Launcher类是所有类加载器的启动器
其中loadClass的类加载过程有如下几步:
加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载
类加载流程:
可以使用javap -v Math.class 指令来查看解析的符号(字面量),这些与字节码文件是一一对应的。
public class TestDynamicLoad {static {System.out.println("*************load TestDynamicLoad************");
}public static void main(String[] args) {new A();System.out.println("*************load test************");B b = null; //B不会加载,除非这里执行 new B()
}
}class A {static {System.out.println("*************load A************");}public A() {System.out.println("*************initial A************");}}class B {static {System.out.println("*************load B************");}public B() {System.out.println("*************initial B************");}}运行结果:*************load TestDynamicLoad*************************load A*************************initial A*************************load test************
程序运行时,所依赖的war包都会被加载使用吗?
不会加载,因为是用懒加载实现的。
先load主类 -->加载被new的类 (类似B b=null的B类并不会加载,静态代码块也不会执行)
类加载器和双亲委派机制
上面的类加载过程主要是通过类加载器来实现的,Java里有如下几种类加载器
双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载
1.一般加载在项目目录下的/terget目录下
2.加载器之间为父子加载器关系,而不是父子类关系(无继承关系)。所有类加载器都继承自ClassLoader类,APP/EXTRA~ 继承自 URL~ 继承自 SECURE~ 继承自 ClassLoader。
源码关键方法:
findLoadedClass(name):检查请求的类是否已经被加载过了(类加载器的存储空间)
- 已经加载过的类会存放在加载它的的类加载器的内存中
findClass():真正加载类的逻辑,通过类加载路径找到类,并defineClass()( 重写就是自定义类加载器 )
loadClass(String,Boolean):如果父类加载器加载失败则会调用自定义的findClass方法( 重写就是打破双亲委派 ) 嵌套向上
defineClass(): 把类字节数组变成类
为什么要设计双亲委派机制?
全盘负责委托机制
“全盘负责”是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入。
自定义类加载器示例:
自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。
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);//defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}}public static void main(String args[]) throws Exception {//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoaderMyClassLoader classLoader = new MyClassLoader("D:/test");//D盘创建 test/com/tuling/jvm 几级目录,将User类的复制类User1.class丢入该目录Class clazz = classLoader.loadClass("com.tuling.jvm.User1");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}运行结果:
=======自己的加载器加载类调用方法=======
com.tuling.jvm.MyClassLoaderTest$MyClassLoader
打破双亲委派机制
再来一个沙箱安全机制示例,尝试打破双亲委派机制,用自定义类加载器加载我们自己实现的 java.lang.String.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("D:/test");//尝试用自己改写类加载机制去加载自己写的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());}
}运行结果:
java.lang.SecurityException: Prohibited package name: java.langat java.lang.ClassLoader.preDefineClass(ClassLoader.java:659)at java.lang.ClassLoader.defineClass(ClassLoader.java:758)