类加载流程
加载
1.通过类的全限定名获取定义该类的二进制字节流
2.将二进制字节流的类的数据结构存入方法区
3.生成class对象,在堆中,后续可以通过这个类对象访问类的信息
第一点中没有限定获取来源,所以可以从zip、网络、运行时生成(动态代理)等方式生成类
验证
文件格式验证、元数据验证、字节码验证、符号引用验证
验证是非必须操作,如果引用的类无需验证,可以采用-Xverifynone参数来关闭大部分的类验证措施
准备
初始化类的静态变量,在方法区中分配内存
只有static类变量会被初始化,初始默认值是数据类型的默认值(0,0l,null,false等),而不是代码中显式赋予的值
如果变量被final和static修饰,那么会被初始化为代码中的值,但是如果变量赋值时使用了方法而不是直接的字面量,方法的调用需要在相应的类结构中执行,因此会是在下述初始化阶段阶段赋值
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
初始化
初始化静态变量和执行static静态块。主要通过构造器方法<client>()执行static块和static变量
初始化顺序为源文件编码顺序,父类先,子类后
如果没有类变量和static块进行赋值,那么不会生成<client>()方法
<clinit>()方法是线程安全的,只会有一个线程执行,其他线程被阻塞
<clinit>()方法和<init>()方法区别为,init是构造函数执行时执行的,在new对象时使用,初始化的是非静态变量
类加载器
启动类加载器(Bootstrap ClassLoader)
c/c++实现
加载$JAVA_HOME\jre\lib和-Xbootclasspath参数指定路径下的类库,所有的java.*开头的类均被Bootstrap ClassLoader加载。启动类加载器无法被java程序引用
扩展类加载器(Extension ClassLoader)
java编写
加载$JAVA_HOME\jre\lib\ext和java.ext.dirs系统变量指定的路径中的类库,如javax.*开头的类,扩展类加载器可以被开发者直接使用
应用程序类加载器(Application ClassLoader)
java编写
加载用户类路径ClassPath指定的类
自定义类加载器
JVM自带的类加载器只会从本地文件系统加载class文件,自定义可以实现其他
用处
1.扩展加载源,比如从数据库、网络
2.防止源码泄露,编译的时候加密掉,需要在类加载时解密
3.隔离加载类
加载顺序:jre核心类 - 启动参数规定类 - 本项目类路径下class文件 - 引入的jar包
双亲委派
好处一,安全,系统核心类只能由上层加载器加载,下层允许加载
好处二,避免重复加载,核心是规定了加载顺序。约定了加载循序后,已经加载的就不用再加载了。反面例子为:不约定顺序,随便找加载器加载的话,一个加载器加载后,另一个加载器也可能再加载一样的类文件
使用线程上下文加载器可以破坏双亲委派,可以让父类加载器请求子类加载器去完成类加载的动作