java类加载运行过程
2020-06-16 10:08:05 0 举报
AI智能生成
java类加载原理及过程
作者其他创作
大纲/内容
四种类加载器
启动类加载器(Bootstrap ClassLoader)
加载JVM自身需要的类
最顶层的类加载器
用c++实现,是虚拟机的部分
负责将<JAVA_HOME>/lib路径下的核心或-Xbootclasspath参数指定的路径下的jar包加载到内存中
按照文件名识别加载jar包,如rt,jar
只加载包名为java,javax,sun等开头的类
java9之后,扩展类加载器(Extension ClassLoader)【扩展类加载器被改名为平台类加载器(platform class loader)】
sun.misc.Launcher&ExtClassLoader
是Launcher的静态内部类,JAVA实现
加载<JAVA_HOME>/lib/ext目录或-D java.ext.dir指定路径的类
应用程序类加载器(Application ClassLoader)
sun.misc.Launcher&AppClassLoader
加载java -classpath或-D java.class.path指定路径下的类库,即classpath路径
一般情况下默认使用该加载器
通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器
自定义加载器
父类是AppClassLoader
一般继承URLClassLoader,不用自己写findClass方法
双亲委派模型
定义:如果一个类接受到类加载请求,他自己不会去加载这个请求,而是将这个请求委派给父类加载器,这样一层一层传送,直到到达启动类加载器。只有当父类加载器无法加载这个请求时,子加载器才会尝试自己去加载
除了顶层的启动类加载器,其余的类加载器都会尝试由父类加载器加载
组合关系,非继承关系
层级由低到高:自定义类加载器->系统类加载器->扩展类加载器->启动类加载器
java1.2之后引入
优势
Java类获得了带有优先级的层次关系
安全,核心api中定义的类型不会被随意替换
使用核心API包名编写自定义类,加载时会报异常
java.lang.SecurityException:Prohibited package name:xxx
双亲委派委派模型的破坏者
线程上下文类加载器
通过java.lang.Thread类中的getContextClassLoader(ClassLoader cl)方法来获取和设置线程的上下文类加载器
如果没有手动设置上下文类加载器,线程将继承其父线程的上下文加载器
初始线程的上下文类加载器是应用程序类加载器(AppClassLoader)
在线程中运行的代码可以通过此类加载器来加载类和资源
类加载过程
加载
加载方式
1.从本地系统直接加载
2.通过网络下载class文件
3.从zip,jar等归档文件中加载class文件
4.从专有数据库提取class文件
5.将java源文件动态编译成class文件
JVM处理过程
1.通过一个类的全限定名来获取此类的二进制流
2.将这个字节流代表的静态存储结构转化为方法区的运行时数据结构
3. 在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口
连接
1.验证
目的:确保JVM加载字节码符合规范
验证分为
- 文件格式验证
- 元数据验证:是否符合java语言规范
- 字节码验证,确保程序语义合法,符合逻辑
- 符号引用验证,确保下一步的解析能正常执行
2.准备
目的:为被加载类的静态变量分配内存,并设置默认初始值
比如int的默认值是0、并分配4个字节的内存空间,引用对象的默认值是null
static int i=5,这里只初始化为0,最后一步初始化时在赋值为5
不包含final,final修饰是常量,final字段在编译时已经赋值完毕
不包含实例变量,实例变量在java堆中
类变量分配在方法区中
比如int的默认值是0、并分配4个字节的内存空间,引用对象的默认值是null
static int i=5,这里只初始化为0,最后一步初始化时在赋值为5
不包含final,final修饰是常量,final字段在编译时已经赋值完毕
不包含实例变量,实例变量在java堆中
类变量分配在方法区中
3.解析
符号引用转换成直接引用
对类的字段和方法进行转换
符号引用:代码在编译阶段,虚拟机并不知道具体引用的地址,所以会用符号引用替代
直接引用:在内存中,将符号引用变成具体的引用地址,可以理解成一个指针,偏移量或者句柄
对类的字段和方法进行转换
符号引用:代码在编译阶段,虚拟机并不知道具体引用的地址,所以会用符号引用替代
直接引用:在内存中,将符号引用变成具体的引用地址,可以理解成一个指针,偏移量或者句柄
初始化
对类的静态变量初始化为指定的值,执行静态代码块
什么时候初始化
- 当虚拟机启动时,初始化用户指定的主类
- 使用new 该类实例化对象的时候
- 读取或设置类静态字段的时候(但被final修饰的字段,在编译时就被放入常量池的静态字段除外static final),初始化该静态字段所在的类
- 调用类静态方法的时候,初始化该静态方法所在的类
- 使用反射class.forName("xxx")对类进行反射调用的时候,该类需要初始化
- 子类的初始化会触发父类的初始化(注:1.接口除外,父接口在调用的时候才会被初始化;2.子类引用父类静态字段,只会引发父类初始化)
- 如果一个接口定义了default方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化
- 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类
初始化顺序【初始化超类-》执行静态初始化器-》类变量初始化】
1.父类的静态变量和静态块赋值(按照申明顺序赋值)
2.自身的静态变量和静态块赋值(按照申明顺序)
3.父类的成员变量和块赋值(按照申明顺序)
4.父类构造器赋值:如果父类中包含有参构造器,则在子类构造器中一定要使用"super(参数)"指定调用父类有参的构造器,不然会报错
5.自身成员变量和块赋值(按照申明顺序)
6.自身构造器赋值
使用
卸载
类与类加载器
两个class是否是同一个类对象
包名必须一致
加载这个类的ClassLoader必须一致
需要覆写loadClass()方法,否则同名类不会加载
可以用该特性实现热部署功能
显式加载与隐式加载
指的是jvm把class文件加载到内存的方式
显式加载:在代码中通过调用ClassLoader加载class对象
Class.forName(name)
this.getClass().getClassLoader().loadClass()
隐式加载:不直接在代码中调用CassLoader的方法加载class对象,而是通过虚拟机自动加载到内存中
在加载某个类的class文件时,该类的class文件中引用了另外一个对象,此时额外引用的类将通过JVM自动加载到内存中
0 条评论
下一页