jvm执行

加载

加载时机

1
加载-->[验证-->准备-->解析](连接过程)-->初始化-->使用-->卸载
1
2
3
4
5
6
7
8
9
10
11
1,遇到new,getstatic,putstaic,invokestatic这四条字节码指令,如果类型没有进行初始化,则需要先触发其初始化阶段.
能够生成这四条指令单典型场景:
使用new关键字实例化对象的时候
读取或设置一个类型的静态字段(被final修饰,已在编译器把结果放入常量池的静态字段除外)的时候
调用一个类型的静态方法大时候.
2,使用java.lang.reflect包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则需要先触发其初始化
3,当初始化类的时候,如果发现其父类没有进行过初始化,则需要先触发其父类的初始化
4,当虚拟机启动时,用户需要制定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类.
5,当时用jdk7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic,REF_putStatic,REF_
invokeStatic,REF_newInvokeSpecial四中类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化.
6,当一个接口中定义了jdk8新加入的默认方法(被default关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化.

加载过程

1
2
3
4
完成3件事.
1,通过一个类的全限定名来获取定义此类的二进制字节流.
2,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构.
3,在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口.

验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1,文件格式验证
验证字节流是否符合Class文件格式的规范

是否以魔数开头
主,次版本号是否在jvm接受范围内
常量池的常量中是否有不被支持的常量类型(检查常量tag标志)
指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量
CONSTANT_Utf8_info类型的常量中是否有不符合UTF-8编码的数据
Class文件中各个部分及文件本省是否有被删除的或附加的其他信息
2,元数据验证
字节码描述的信息进行语义分析

是否有父类(除object外,所有类都应该有父类)
这个类的父类是否继承了不允许被集成的类(被final修饰的类)
若不是抽象类,是否实现类其父类或接口实现的所有方法
类中的字段,方法是否与父类产生矛盾
3,字节码验证
通过数据流分析和控制流分析,确定程序语义是合法的符合逻辑的
4,符号引用验证
发生在虚拟机将符号引用转换为直接引用的时候,这个转换动作将在连接的第三阶段--解析阶段中发生

准备阶段

1
正式为类中定义的变量(静态变量,static修饰的变量)分配内存并设置类变量初始值的阶段

解析过程

1
2
3
4
5
6
7
8
9
10
11
12
虚拟机将常量池内的符号引用替换为直接引用的过程

符号引用:符合引用以一组符号来描述索引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可.
符号引用与虚拟机实现的内存布局无关,引用的目标并不一定是已经加载到虚拟机内存中的内容.各个虚拟机实现的内存布局可以各不相同,但是他们能接受的符号引用必须都是一致的.因为符号引用的字面量形式已经明确在<<java虚拟机规范>>的class文件格式中.
直接引用:直接引用是可以直接执行目标的指针,相对偏移量或者是一个能间接定位到目标的句柄.
直接引用是和虚拟机实现的内存布局直接相关,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同,如果有了直接引用,那引用的目标必定已经在虚拟机的内存中存在.

解析内容
1,类或接口的解析
2,字段解析
3,方法解析
4,接口方法解析

初始化

1
2
进行准备阶段时,变量已经赋过一次系统要求的初始零值,而在初始化阶段,则会根据程序员通过程序编码指定的主观计划去初始化类变量和其他资源.
我们也可以从另外一种更直接的形式来表达:初始化阶段就是执行类构造器<clinit>()方法的过程.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
父                子
------------------------------首次
static{}
static{}
code{}
构造{}
code{}
构造{}
-------------------------------
code{}
构造{}
code{}
构造{}
------------------------------
code{}
构造{}
code{}
构造{}
------------------------------

类加载器

双亲委派模型

1
2
3
启动类加载器(bootstrap)-->扩展类加载器(extension)-->应用加载器(application)-->自定义1加载器
findclass:父加载器没有才会调用该方法
loadclass:直接进行加载类 -->自定义2加载器

破坏双亲委派

1
2
3
4
5
1,jdk1.2之前,双亲委派之前
2,基础类是父加载器,实现类是其他类提供.
比如spi(jndi,jdbc,jce,jaxb,jbi),它的代码是启动类来加载,
配置一个线程上下文类加载器,如果创建线程时还未设置,它将会从父线程急成一个,如果在应用程序的全局范围内都没有设置过,那这个类加载器默认就是应用程序类加载器.
3,由于用户对程序动态性的追求导致的.代码热替换,模块热部署等