目录
1、加载 (加载字节码文件,生成.class对象)
2、验证(验证Class文件是否符合规范)
3、准备 (为静态变量分配内存并设置变量初始值)
4、解析(初始化常量池中的一些常量)
5、初始化(初始化对象,并为静态变量赋值)
总结:
双亲委派模型:
JVM的类加载器(主要有3个):
标准库中的String类是怎样被加载的?
自定义的类如何加载?
类加载一定要使用双亲委派模型吗?
为什么Tomcat不使用双亲委派模型?
Tomcat是怎么实现webapps下各个web应用程序的隔离的?
类加载过程简单来说就是把.class文件加载到内存中构造出类对象
JVM的类加载过程要经历以下几个阶段:加载->连接->初始化
其中连接阶段可以细分成:验证->准备->解析
所以也可以说JVM的类加载过程是这几个阶段: 加载->验证->准备->解析->初始化
1、加载 (加载字节码文件,生成.class对象)
首先要进行加载的过程。加载过程就是要找到.class文件,打开并读取它。在这个过程中要先从.class文件中获取到这个类的二进制字节流,然后将这个字节流代表的静态存储结构转化为方法运行时的数据结构,最后在内存中生成一个代表该类的java.long.Class对象,然后把这个放到方法区,作为该类的数据访问接口。
二进制的.class文件包含内容:
详情见Java虚拟机规范手册:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html
2、验证(验证Class文件是否符合规范)
在进行读取前,要先验证Class文件字节流中包含的信息是否符合Java虚拟机规范的约束条件, 验证的内容包括:文件格式,字节码,符号引用
3、准备 (为静态变量分配内存并设置变量初始值)
准备阶段是正式为类中的属性(static修饰的变量)分配内存和进行初始值的设定
如:public static int num=123;在准备阶段并不会对num进行赋值123,而是先设定num=0;
4、解析(初始化常量池中的一些常量)
前面我们说到.class文件中包含的内容,在这一阶段,JVM会将符号引用转化为直接引用,也就是在这一阶段,会对常量进行一个初始化。
5、初始化(初始化对象,并为静态变量赋值)
在这一阶段,Java虚拟机开始真正的执行类中的代码,进行对象的初始化,并且为静态变量赋值,比如前面的 num 就会在这一阶段被赋值为123,而不是原来的0了。
总结:
双亲委派模型:
双亲委派模型属于一种类加载的机制。
前面我们说到了类加载的过程经历了加载-> 连接->初始化的过程。其中加载的过程是加载字节码文件,生成.class对象。在这个加载的过程中类加载器发挥着至关重要的作用,而双亲委派模型就是描述类加载器是如何根据全限定名(类名+包名,如java.long.String)找到.class文件的过程,这个过程属于加载过程的靠前的阶段。
JVM的类加载器(主要有3个):
JVM的类加载器主要是下面这3个:
BootSharpClassLoader(负责加载标准库当中的类(如List,String))
ExtensionClassLoader(负责加载JDK扩展库当中的类)
ApplicationClassLoader(负责加载当前目录当中的类)
除此之外,用户也可以自定义类加载器:User-DefinedClassLoader,一般没有使用自定义加载器,所以默认就是上面的那3种类加载器。每个类加载器负责加载自己负责的目录。
下图来源于博客: JVM类加载器-CSDN博客
双亲委派模型就是描述了上述的类加载器之间是怎么配合的 。
例子(没有自定义类加载器):
标准库中的String类是怎样被加载的?
第一步:程序启动,进入ApplicationClassLoader类加载器
第二步:在ApplicationClassLoader中检查其父类加载器ExtensionClassLoader是否被加载过,如果没有加载过就进入到ExtensionClassLoader中
第三步:在ExtensionClassLoader中检查最高父类加载器BootSharpClassLoader是否加载过,没有就进入BootSharpClassLoader中。
最后在BootSharpClassLoader中查找标准库的目录:java.long.String,完成String的加载。
自定义的类如何加载?
如果是自定义的类,也会经历上述过程,但是由于在BootSharpClassLoader目录中无法找到该自定义的类就会回到上一级ExtensionClassLoader中进行加载,如果还是找不到就会到上一级ApplicationClassLoader当中进行加载,然后再当前目录中找到了自定义的类进行加载。
如果在最后没有成功加载就会抛出ClassNotFoundException。
双亲委派模型的优势:
当用户自定义的类与标准库中的类的全限定名重复了,依然可以准确的加载标准库中的类,而不是自定义的类,比如自定义一个和标准库中的java.long.String的一样的类,当类加载器加载到BootSharpClassLoader的时候就不会再返回了,就加载的是标准库中的类。
类加载一定要使用双亲委派模型吗?
不一定,因为双亲委派模型只是类加载机制中的一种,是适用于JVM的类加载的,而像Tomcat加载webapps中的类则没有使用双亲委派模型。
为什么Tomcat不使用双亲委派模型?
首先Tomcat中的webapps下面有很多的web应用程序。
第一个原因:隔离性,为了保证每个web应用程序之间互不干扰,可以相互独立互不干扰的加载和销毁各自的类,不会因为web程序因为重名或者版本不同等原因造成冲突。
第二个原因:方便动态的重新加载已经加载过的类,使得开发和部署的效率更高。
Tomcat是怎么实现webapps下各个web应用程序的隔离的?
Tomcat自定义了一个webapp类加载器,并且给每一个web应用程序都创建一个类加载器,所以不同的类加载器加载的就是不同的类了。