JVM是什么
java从编码到执行的过程:
java文件通过javac命令转换为.class文件,然后会被classLoader转载到内存中,可能.class文件还包含某些java类库,类库也会被转载到内存中,转载完之后就会通过字节码解释器或者JIT即时编译器进行解析,最后通过执行引擎进行运行。
JVM与JAVA无关:
任何语言只要能编译成class文件,它都能在JVM上运行
JDK、JRE、JVM的关系
类加载的三个过程
类加载的过程分为三部分:
1、加载过程
2、连接过程
3、初始化过程
加载过程采用的是双亲委派机制
双亲委派机制实际上是在ClassLoader中的loadClass方法中实现的
要了解双亲委派机制,首先要了解类加载器的关系:
解释:
CustomClassLoader的父加载器是APPClassLoader,
APPClassLoader的父加载器是ExtensionClassLoader,
ExtensionClassLoader的父加载器是BootstrapLoader
加载过程的双亲委派解释:
第一步在缓存中找需要加载的文件
.class文件通过custom.loadClass加载进去后,现在Custom加载器的缓存中找,找着则返回结果,没有找着,到父加载器的缓存进行查找,以此类推,最后到Bootstrap加载器中进行查找,如果还是不能在缓存中找到,那么就执行第二步
第二步:通过每个类加载器执行不同的功能,对.class文件进行加载
每一次父加载器找不到加载的文件就到子加载器中进行查找,以此类推
为什么使用双亲委派机制进行加载
使用双亲委派机制很大程度上保证了安全,每次加载的过程中,最终都会被Bootstrap ClassLoader所响应,加载的Object类也只会出现一个,基于这种机制,最后才会是自定义的ClassLoader,所以用户自定义的类不会影响原来的类。
打破双亲委派机制问题
在什么情况下打破双亲委派机制:
1、JDK1.2之前,自定义的ClassLoader都必须重写loadClass()
2、ThreadContextClassLoader(线程上下文类加载器)可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定,经典案例就是jdbc
3、热启动热部署,像tomcat,osgi都有自己的模块指定classLoader,如果一个项目中有不同版本的jar包,按照双亲委派机制处理的话,只会加载第一个jar包,后面的jar包因为同名,不会被加载进去,但是像tomcat这种的是可以加载同一类库的不同版本
怎么打破双亲委派机制:重写loadClass()方法即可
类加载的连接过程(Linking)
1、Verification校验过程
- 验证文件是否符合JVM规定
2、Preparation准备过程
- 静态成员变量赋予默认值
3、Resolution解析过程
- 将类、方法、属性等符号引用解析为直接引用,常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
类加载的初始化过程(Initializing)
给静态成员变量赋初始值
public class Test {
public static void main(String[] args) {
System.out.println(P.num);
}
}
class P{
public static int num = 10;
public static P p = new P();
public P() {
num++;
}
}
输出结果:
public class Test {
public static void main(String[] args) {
System.out.println(P.num);
}
}
class P{
public static P p = new P();
public static int num = 10;
public P() {
num++;
}
}
输出结果: