jdk-虚拟机
从java.exe 开始讲投java类加载运行全过程
jdk远吗级别剖析jvm核心类加载器
jdk源码级别剖析类加载双亲委派机制
手写自定义类加载器打破双亲委派机制
tomcat了加载机制深度剖析
手写tomcat类加载器实现多个版本代码共存隔离
classLoad.loadClass(.class) ==> 类 如果加载在虚拟机上的
验证 字节码文件是否正确,有规范
javap -v Math.class ,下面就是 二进制的另外一种现实方式,更容易查看
准备:
静态变量 赋予初始值,,注意是变量,如果是常量是直接赋值的
解析:
关于符号引用转为直接引用(符号到内存地址的转换)
静态链接:比如静态方法
动态链接:运行时,才去解析,比如多态,只有运行时候才知道调用哪个方法
main 在类加载阶段,符号转为直接引用
math.compute() 符号是在常量池里,在运行阶段 回去解析,将符号解析为直接引用
java -p 一下: 解释下动态链接math.compute()
class 文件的 invokevirtual #4 对应就是java 文件的math.compute()
这些字节码文件都是静态的,符号也都是静态的
代码加载到jvm内存中之后 就放到方法区,这些方法在内存中就会有地址,一旦这些符号加载到jvm内存,这些常量池就会变成运行时常量池,都有内存地址指向 ,math.compute 只有在main方式执行的时候才会去解析 math.compute这个符号,根据这个符号去到内存找对应的内存位置找到compute方法。
static 方法是不变的,在类加载的时候 就直接将符号转为地址
方法区,Class对象放到堆里面
类加载器
单例创建 Laucher ,获取得到ext类加载器和ap类加载器,都是类对象,每个类加载器都有一个parent,即父类加载器(除了根类加载器,c++写的)
源码对应路径:打印 app类加载器加载类的文件夹,包含根和扩展类加载器下的文件下 ,类都是交给应用类加载器,然后不能加载的通过双亲委派:
双亲委派:
1、判断是否已经加载过得,底部是通过 private native final Class<?> findLoadedClass0(String name); 的一个native方法。AppClassLoader 加载java.lang.String 通过该方法可直接判断已被加载过,那么就直接返回,无需调用父加载器(非父类,不是继承关系)
自己写个java.lang.String
首先交给appClassloader,没有加载过,交个ext 最后交给root ,因为根类加载器加载lib包,string是没有main这个方法,因此抛出错误。
自定义类加载器的父加载器是应用类加载器,因此自定义加载器会先交给应用类加载器加载
自定义类加载器的步骤:
1:继承ClassLoader
2:实现finClass方法
defineClass native方法,传递二进制字节码文件进去
在D盘的test文件夹下放置:
打破双亲委派
重写父类的 java.lang.ClassLoader#loadClass(java.lang.String, boolean) 方法
删除双亲委派的逻辑,报错:
Object类是所有类的父了,然后自定义类加载器加载User对象的时候,也会用这个类加载器去加载Object对象,指定的classPath D://test 下没有这个class文件,所以报错