作者:君慎豫行 | 来源:互联网 | 2023-09-15 11:56
在java中数据类型分为基本数据类型和引用数据类型。基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载。
按照java虚拟机规范,从class文件到加载进入内存中的类,再到类卸载出内存为止,整个生命周期如下
一、加载
将java类的字节码文件加载到机器内存中,并在内存中构建出java类的原型(类模板对象)
1、加载类时,java虚拟机加载步骤
(1)通过类的全名,获取类的二进制数据流
(2)解析类的二进制数据流为方法区内的数据结构(Java类模型)
(3)创建java.lang.Class类的实例,作为方法区这个类的各种数据的访问入口
2、类模型和Class实例的位置
(1)类模型存储在方法区
(2)class文件加载到元空间后,会在堆中创建一个Class对象,用来封装类位于方法区内的数据结构。每一个类都对应一个Class对象
Class类的构造方法是私有的,只有jvm可以创建。
3、数组类的加载
数组类本身并不是由类加载器负责创建,而是由jvm在运行时根据需要直接创建的,但是数组的元素类型仍然需要依靠类加载器去创建。创建步骤如下
(1)如果数组的元素类型是引用类型,那么遵循定义的加载过程递归加载和创建数组A的元素类型
(2)jvm使用指定的元素类型和数组维度类创建新的数组类。
二、链接
1、验证
保证加载的字节码是合法的。
验证通过之后类加载器才会将类的二进制数据加载到方法区中,除格式验证之外的验证操作是在方法区中进行
(1)格式验证
是否以魔数0xCAFEBABE开头,主版本和副版本是否在当前的java虚拟机的支持范围内等。
(2)语义检查
是否所有的类都存在父类(在java里,除了Object之外,其他类都有父类)
是否一些被定义为final的方法或者类被重写或者继承了
非抽象类是否实现了所有抽象方法或者接口方法
是否存在不兼容的方法
(3)字节码验证
在字节码执行的过程中,是否会跳转到一条不存在的指令(不能达到100%验证)
函数的调用是否传递了正确类型的参数
变量的赋值是不是给了正确的数据类型等
注:
栈映射帧(StackMapTable)是处于该阶段,在检测特定的字节码时,其局部变量表和操作数栈是否有着正确的数据类型。
(4)符号引用验证
Class文件在常量池会通过字符串记录自己将要使用的其他类或者方法。在验证阶段,虚拟机会检查类或者方法是否存在,访问权限是否受限等。如果一个类在系统中找不到,则抛出NoClassDefFoundError,如果一个方法无法找到,则抛出NoSuchMethodError。
2、准备
当一个类通过验证,就会进入准备阶段,为静态变量分配内存,并初始化默认值。
java不支持boolean类型,内部实现是int,由于int默认值为0,所以boolean的默认值为false.
3、解析
将接口,字段和方法的符号引用转换为直接引用。
java虚拟机为每一个类都准备一张方法表,将其所有的方法列在其中,当需要调用一个类的方法时,只要知道这个方法在方法表的偏移量(通过解析操作,符号引用转换为直接引用类中的方法表中的位置),就可以直接调用该方法了。
不过java虚拟机规范并没有明确要求解析阶段一定要按序执行,在hotspot中,按照加载,验证,准备和初始化的顺序执行,但链接阶段的解析操作往往伴随着jvm执行完初始化之后再执行。
三、初始化
类的初始化时类装载的最后一个阶段,也是真正开始执行类定义的java的程序代码()方法
在加载一个类之前会先加载其父类,因此父类的方法总是在子类方法之前被调用。
1、使用static+final修饰,且显示赋值中不涉及到方法或者构造器调用的基本数据类型或者String类型的显示赋值,是在链接阶段的准备环节进行赋值。
public static int a = 1;//在初始化阶段()中赋值
public static final int INT_CONSTANT = 10;//在链接阶段的准备环节赋值public static final Integer INTEGER_CONSTANT1 = Integer.valueOf(100);//在初始化阶段()中赋值
public static Integer INTEGER_CONSTANT2 = Integer.valueOf(1000);//在初始化阶段()中赋值public static final String s0 = "helloworld0";//在链接阶段的准备环节赋值
public static final String s1 = new String("helloworld1");//在初始化阶段()中赋值public static String s2 = "helloworld2";public static final int NUM1 = new Random().nextInt(10);//在初始化阶段()中赋值
2、线程安全性
虚拟机会保证一个类的()方法在多线程环境中被正确的加锁,同步。
class StaticA {static {try {Thread.sleep(1000);} catch (InterruptedException e) {}try {Class.forName("com.atguigu.java1.StaticB");} catch (ClassNotFoundException e) {e.printStackTrace();}System.out.println("StaticA init OK");}
}
class StaticB {static {try {Thread.sleep(1000);} catch (InterruptedException e) {}try {Class.forName("com.atguigu.java1.StaticA");} catch (ClassNotFoundException e) {e.printStackTrace();}System.out.println("StaticB init OK");}
}public class StaticDeadLockMain extends Thread {private char flag;public StaticDeadLockMain(char flag) {this.flag = flag;this.setName("Thread" + flag);}@Overridepublic void run() {try {Class.forName("com.atguigu.java1.Static" + flag);} catch (ClassNotFoundException e) {e.printStackTrace();}System.out.println(getName() + " over");}public static void main(String[] args) throws InterruptedException {StaticDeadLockMain loadA = new StaticDeadLockMain('A');loadA.start();StaticDeadLockMain loadB = new StaticDeadLockMain('B');loadB.start();}
}