JVM类的加载分三步走:加载(load)->链接(link)->初始化(init)。
加载
通过类的全限定名获取定义该类的二进制字节码文件
我们知道.class文件有几种常见的加载方式:- 本地系统直接加载
- 网络获取
- 从zip压缩包中读取(jar,war包)
- 运行时计算生成(动态代理)
- 由其他文件生成(JSP)
- 从专有数据库中提取(比较少)
- 从加密文件中获取(防止.class文件被反编译)
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区中这个类的各种数据的访问入口
链接
链接也分三步走:验证—>准备->解析
- 验证
确保class文件的字节流中包含的信息符合当前虚拟机的要求,保证被加载类的正确性,保护虚拟机安全。
验证主要有四种:文件格式验证,元数据验证,字节码验证,符号引用验证 准备
为类变量(静态变量)分配内存(到方法区),并设置默认初始值(零值)
特例:- 这里不包含final修饰的静态变量,因为final变量在编译时就会分配内存空间(因为它是常量),准备阶段会显式初始化(直接初始化为代码中赋予的值)。
- 这里也不包含实力变量,因为实例变量是随着对象分配到堆中的。
- 解析
将常量池中的符号引用转换为直接引用。
实际上解析操作通常在初始化之后再执行。
符号引用:一组符号来描述所引用的目标。
直接引用:直接指向目标的指针,相对偏移量或句柄。
主要针对类或接口,字段,接口方法,方法类型等。
初始化
- 执行类构造器方法<clinit>()的过程。
- 这个方法不需要定义,由javac编译器收集类中的所有静态变量的赋值动作和静态代码块中的语句合并而来(既类中静态的操作,所以叫初始化)
- 构造器方法中的指令按语句在源文件中出现的顺序执行(即从上至下,先出现的先执行)
- 如果有父类,就先执行父类的<clinit>()
- 在多线程环境下,一个类的<clinit>()方法会被加锁。这是因为一个类只需要初始化一次,防止多线程环境下被多次初始化。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。