JVM 从入门到上天之底层类加载

klass模型

地球人都知道jvm底层是c++实现的,c++同样是面向对象的,所以我们上层的java对象必然会对应着底层的一个c++对象,显而易见,这个klass就是c++中对象的表现形式,接下来我们先来看看klass模型类的继承结构。


instanceKlass

对应的普通的java类

接下来说一下它的三个子类

1、InstanceMirrorKlass

用来表示java.lang.Class,是jvm暴露给程序使用的,也就是说我们代码中使用的对象就是这个实例,它是存储在堆区的,而jvm内部使用的是instanceKlass,它是存储在方法区也就是元空间。(注:这里解释一下,方法区是jvm的规范,元空间是HotSpot的具体实现)

2、InstanceRefKlass

用来表示java.lang.ref.Reference类

3、InstanceClassLoaderKlass

用来遍历某个加载器加载的类

ArrayKlass

表示数组的元信息

1、TypeArrayKlass

基本数据类型数组

2、ObjArrayKlass

引用数据类型数组

类的加载过程

类的生命周期是由7个阶段组成,但是类的加载是说的前五个阶段,看下图所示


加载

过程

1、通过类的全限定名获取存储该类的class文件

2、解析成运行时数据,即instanceKlass实例,存放在方法区

3、在堆区生成该类的Class对象,即InstanceMirrorKlass实例

时机

1、创建类的实例的时候

2、访问某个类或接口的静态变量,或者给该静态变量赋值

3、调用类的静态方法

4、通过反射执行上述三种行为

5、初始化子类时会触发父类的初始化

6、 Java虚拟机启动时被标明为启动类的类(有main方法的类)

7、JDK1.7开始提供的动态语言支持(了解即可)

预加载

包装类、String、Thread

验证

1、文件格式验证

2、元数据验证

3、字节码验证

4、符号运用验证

准备

为静态变量分配内存、赋初值。

如下图各种数据的初始值


如果是被final修饰,在编译的时候会给属性添加ConstantValue属性,准备阶段直接完成赋值,即没有赋初值这一步。

解析

将常量池中的符号引用转为直接引用

解析后的信息存储在ConstantPoolCache类实例中

1、类或接口的解析

2、字段解析

3、方法解析

4、接口方法解析

初始化

执行静态代码块,完成静态变量的赋值

方法中语句的先后顺序与代码的编写顺序有关

类加载细节

子类访问父类的静态字段有两种实现方式

1、先去子类的镜像类中去取,如果有直接返回,如果没有,会沿着集成链将请求往上抛,很明显这种算法的性能随继承链的deep而上升,算法复杂度为O(n)

2、借助另外的数据结构实现,使用K-V的格式 存储,查询性能为O(1)。HotSpot就是使用的第二种方式,借助另外的数据结构ConstantPoolCache,常量池类ConstantPool中有个属性_cache指向了这个结构。每一条数据对应一个类ContantPoolCacheEntry.

ConstantPool/ConstantPoolCache

每个InstanceKlass关联着一个ConstantPool,作为该类型的运行时常量池。这个常量池的结构跟Class文件里的常量池基本上是对应的

ConstantPoolCache主要用于存储某些字节码指令所需的解析(resolve)好的常量项,例如给[get|put]static、[get|put]field、invoke[static|special|virtual|interface|dynamic]等指令对应的常量池项用

你可能感兴趣的:(JVM 从入门到上天之底层类加载)