Jvm的生命周期:
启动:启动一个java程序时,一个jvm实例就产生了,任何一个拥有main()函数的class都可以作为jvm实例运行的起点
运行:main()作为程序运行的起点,任何其他线程均由该线程启动。main函数是非守护线程。
消亡:当程序中所有的非守护线程终止时
代码的执行过程
类加载器:虚拟机自带的三种类加载器分别为启动类加载器(也叫引导类加载器)bootStrapClassLoader,扩展类加载器(ExtentionClassLoader),应用类加载器(也叫系统类加载器)AppClassLoader
启动类加载器加载java核心类库,用于提供jvm自身需要的类
扩展类加载器加载JDK安装目录下的扩展目录里的类
系统类加载器是程序中默认的类加载器,java应用的类都是由他加载
类加载器的特点
- 全盘负责:当一个类加载器加载一个类时,该类所依赖的其他类也会被类加载器加载到内存中
- 缓存机制:所有的Class对象都会被缓存,当程序需要用到某个Class对象时,会先去内存中找,找不到才会从.class文件中去读取数据,转化为class对象加载到内存中
- 双亲委派机制:如果一个类加载器收到了类加载请求,他并不会先自己去加载,而是把这个请求委托给父类加载器去执行,如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最终达到顶层的启动类加载器。如果父类加载器可以完成类加载任务,就成功返回,如果不能,子加载器才会尝试自己去加载。这么做的好处是能够保证一个类的全局唯一性,并且防止了恶意代码影响核心代码
反向委托:当类加载器加载第三方jar时,比如spi接口(外设接口),这个接口属于核心api,首先通过双亲委派机制将类加载请求委托到引导类加载器,引导类加载器去加载spi核心类,spi接口中使用有第三方的jar包jdbc.jar,第三方的jar需要使用线程上下文类加载器(应用类加载器)去加载,这个步骤就叫反向委托。
沙箱安全机制:沙箱安全机制将java代码限定在jvm的特定运行范围中,并且严格限制代码对本地系统资源进行访问,通过这种方式实现对java代码的有效隔离,防止对本地操作系统造成破坏
类的主动使用和被动使用(主动使用会引起类的初始化)
主动使用的7种情况:
1.创建类的实例
2.访问类或接口的静态变量或者对该静态变量赋值
3.调用类的静态方法
4.反射
5.初始化一个类的子类
6.jvm启动时被标明为启动类的类
7.JDK 7开始提供的动态语言支持:java.lang.invoke. MethodHandle实例的解析结果REF getstatic, REF putstatic, REF invokestatic句柄对应的类没有初始化,则初始化
其他情况均为被动引用
类的加载过程
加载(即通过类加载器加载):类加载器加载.class文件转换成二进制数据存储在jvm的运行时数据区的方法区中,并在堆中生成class对象
验证:对文件格式,元数据,字节码,符号引用做相应的验证,这些验证是分布在不同的环节下进行的,验证的目的就是保证加载的代码不会对jvm产生伤害
准备:对class中的静态变量进行赋值,值类型赋0,引用类型赋null。
ConstantValue属性:当基本类型或String被static final修饰时,javac会在编译期为该常量赋上ConstantValue属性,在类加载的准备阶段,虚拟机便会为拥有该属性的常量设置值
解析:对class的符号引用替换为直接引用
解析分为静态解析和动态解析
静态解析:当class引用的是一个具体的实现类,例如A引用了B,那么当A加载时发现B没有加载,此时就会马上加载B,加载完成后会把B的符号引用替换为B的真实地址
动态解析:当class引用的不是一个具体的实现类,而是抽象类或者接口,那么当程序执行到需要调用引用的类时,才会触发解析
初始化:执行程序中的初始化赋值操作,例如静态变量的赋值,静态代码块等