Klass模型
Java的每个类,在JVM中都有一个对应的Klass与之对应 ,存储类的元信息如:常量池、属性信息、方法信息……
Java中创建普通Java对象类对应在jvm存在形式为: InstanceKlass
Java创建array数组在对应在jvm存在的行为为 :ArrayKlass
InstanceMirrorKlass:是用来表示java.lang.Class,java代码中获取到的Class对象,实际上就是这个C++类的实例,存储在堆区,学名:镜像类。
InstanceRefKlass:用来表示java.lang.ref.Reference类的子类。
InstanceClassLoaderKlass:用于遍历某个加载器加载的类
Java数组的元信息用ArrayKlass的子类来表示:
TypeArrayKlass是进行存储 基本类型数据
ObjArrayKlass 是进行存储对象数据(引用类型)
备注:java的数组不是静态数据类型,是动态数据类型,即在jvm运行时动态创建的
类的加载过程
类的生命周期是由7个阶段组成,但是类的加载说的是前5个阶段
加载阶段:
1.通过全限定名加载class文件
2. 解析成运行时数据,即instanceklass数据
何时加载
主动使用时
1、new、getstatic、putstatic、invokestatic
2、反射
3、初始化一个类的子类会去加载其父类
4、启动类(main函数所在类)
5、当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化
预加载:包装类、String、Thread
校验阶段:
1、文件格式验证
2、元数据验证
3、字节码验证
4、符号引用验证
准备阶段:
为静态变量分配内存、赋初值
实例变量是在创建对象的时候完成赋值的,没有赋初值一说
如果被final修饰,在编译的时候会给属性添加ConstantValue属性,准备阶段直接完成赋值,即没有赋初值这一步
解析阶段:
将常量池中的符号引用转为直接引用
解析后的信息存储在ConstantPoolCache类实例中
1、类或接口的解析
2、字段解析
3、方法解析
4、接口方法解析
何时进行解析
思路:
1、加载阶段解析常量池时
2、用的时候
openjdk是第二种思路,在执行特定的字节码指令之前进行解析:
anewarray、checkcast、getfield、getstatic、instanceof、invokedynamic、invokeinterface、invokespecial、 invokestatic、invokevirtual、ldc、ldc_w、ldc2_w、multianewarray、new、putfield
初始化阶段:
执行静态代码块,完成静态变量的赋值
静态字段、静态代码段,字节码层面会生成clinit方法
方法中语句的先后顺序与代码的编写顺序相关
代码示例:
public class Test_1 {
public static void main(String[] args) {
System.out.printf(Test_1_B.str);
}}
class Test_1_A {
public static String str = "A str";
static {
System.out.println("A Static Block");
}}
class Test_1_B extends Test_1_A {
static {
System.out.println("B Static Block");
}}
1、输出是 A Static Block 、A str 将不输出B StaticBlocK ,因为使用对象的过程中并没有涉及到子类的变量
2、如果将str 静态变量定义在子类中则输出的信息则为A Static Block、B Static Block、A str 因为这是使用的变量为子类的信息,这是将会进行初始化
3、如果将子类的str类型加final变量修饰,则只输出 A str
4、如果将子类的str 使用UUID.randomUUID方法赋值 ,则会输出A Static Block、B Static Block、A str ,因为UUID所使用的发法需要进行实例化才可进行赋初始值。
则上述示例则可证明jvm使用的是懒加载
public class Test_1 {
public static void main(String[] args) {
System.out.println(Test_1_A.a);
System.out.println(Test_1_A.b);
}}
class Test_1_A {
public static int a;
public static int b = 1;
public static Test_1_A test1 = getInstance();
public Test_1_A() {
a++; b++; }
public static Test_1_A getInstance(){
return new Test_1_A();
}}
1】输出结果为 1,2 因为在执行实例化对象之前 a默认为0 b默认为 1 执行实例化后进行默认+1操作
2】如果将public static int b = 1; 放在实例化方法之后 则输出为1,1 因为在执行为初始化之后才执行的 调用b 则会将b值进行重新赋值这个结果就验证了 在类加载【方法中语句的先后顺序与代码的编写顺序相关】