JVM底层之类加载

258 阅读5分钟

JVM底层之类加载

安装 Ubuntu

www.jianshu.com/p/54d9a3a69…

HSDB

www.cnblogs.com/alinainai/p…

源码工具 Source Insight

IDEA插件 jclasslib

深入理解Java虚拟机 书。

jsp-l 查询进程ID

kclass模型

Java的每个类,在JVM中,都有一个对应的Klass类实例与之对应,存储类的元信息如:常量池、属性信息、方法信息……

看下klass模型类的继承结构

cdn.nlark.com/yuque/0/202…

class Java类 Java代码 kclass Java类在JVM的存在形式 C++代码

类加载器将.class文件加载进系统 将.class文件解析,生成的是什么?类的元信息在JVM中是如何存储的? InstanceClass

对象的内存结构 kclass pointer:

kclass就是InstanceClass和它的子类

InstanceClass Java类 (非数组)

InstanceMirrorClass 镜像类 class对象(堆区)

InstanceRefClass 引用

ArrayClass 存储数组类的元信息

Java中的数组

1 静态数据类型 JVM中内置的八种数据类型

2 动态数据类型 运行时动态生成

如何证明?

基本类型 newArray 创建一个指定原始类型(int float char)的数组,并将其引用值压入栈顶

TypeArrayKclass 基本类型的数组在JVM的存在形式

引用类型 anewArray 创建一个引用类型(类 接口 数组)的数组 并将其引用值压入栈顶

ObjArrayKclass 引用类型的数组在JVM的存在形式

类的加载过程

cdn.nlark.com/yuque/0/202…

加载

1、通过类的全限定名获取存储该类的class文件(没有指明必须从哪获取)

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

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

程序随便你怎么写,随便你用什么语言,只要能达到这个效果即可 就是说你可以改写openjdk源码,你写的程序能达到这三个效果即可

包名+类名

何时加载??

1、new、getstatic、putstatic、invokestatic

2、反射

3、初始化一个类的子类会去加载其父类

4、启动类(main函数所在类)

5、当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化

JVM加载类是lazyloading

rt.jar

根加载器

预加载

String Thread Interger

测试:

1)

A Static Block A str

为什么没有B Static Block? static静态字段 使用的时候才会加载 (使用的时候才会加载)

public class Test_1 { public static void main(String[] args) { System.out.printf(Test_1_B.str);

    while (true);
}

}

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"); } }

2)

A Static Block B Static Block A str

加载子类 父类一定加载 主动使用子类 父类需要加载 间接在主动使用父类

public class Test_1 { public static void main(String[] args) { System.out.printf(new Test_1_B().str);

    while (true);
}

}

class Test_1_A { static { System.out.println("A Static Block"); } }

class Test_1_B extends Test_1_A { public String str = "A str";

static {
    System.out.println("B Static Block");
}

}

3)静态字段在子类里面

A Static Block B Static Block A str

static静态字段 使用的时候才会加载 (使用的时候才会加载)

public class Test_2 {

public static void main(String[] args) {
    System.out.printf(Test_2_B.str);
}

}

class Test_2_A { static { System.out.println("A Static Block"); } }

class Test_2_B extends Test_2_A { public static String str = "B str";

static {
    System.out.println("B Static Block");
}

}

4)null

只是定义一个类型而已

public class Test_4 {

public static void main(String[] args) {
    Test_4 arrs[] = new Test_4[1];
}

}

class Test_4_A { static { System.out.println("Test_4_A Static Block"); } }

5)

A Str

为什么不加载 Test_6_A Static Block?

final常量

将常量str写入test_6的常量池

public class Test_6 {

public static void main(String[] args) {
    System.out.println(Test_6_A.str);
}

}

class Test_6_A { public static final String str = "A Str";

static {
    System.out.println("Test_6_A Static Block");
}

}

6)

Test_7_A Static Block

UUID.randomUUID().toString() 不在常量池 UUID.randomUUID() 动态生成 Test_7 会主动使用

public class Test_7 {

public static void main(String[] args) {
    System.out.println(Test_7_A.uuid);
}

}

class Test_7_A { public static final String uuid = UUID.randomUUID().toString();

static {
    System.out.println("Test_7_A Static Block");
}

}

7)

Test_8 Static Block A Static Block

反射。

public class Test_8 {

static {
    System.out.println("Test_8 Static Block");
}

public static void main(String[] args) throws ClassNotFoundException {
    Class<?> clazz = Class.forName("com.luban.classload.Test_1_A");
}

}

从哪加载??

1、从压缩包中读取,如jar、war

2、从网络中获取,如Web Applet

3、动态生成,如动态代理、CGLIB

4、由其他文件生成,如JSP

5、从数据库读取

6、从加密文件中读取

验证

1、文件格式验证

2、元数据验证

3、字节码验证

4、符号引用验证

准备

赋初值

cdn.nlark.com/yuque/0/202…

!!!!! final修饰 直接赋值 没有赋初值

解析

间接引用转直接引用

间接引用 指向运行时常量池的引用 直接引用 内存地址

常量池 class文件的常量池(静态) 运行时常量池(HSDB) 字符串常量池 StringTable

初始化

执行静态代码段

1 定义一个static JVM自动生成clinit

2 生成的clinit方法

代码顺序和定义的顺序保持一致

题目:

  1. 1,2

初始化:

val1 = 0 val2 = 1

执行Test_21_A()

public class Test_21 {

public static void main(String[] args) {
    Test_21_A obj = Test_21_A.getInstance();

    System.out.println(Test_21_A.val1);
    System.out.println(Test_21_A.val2);
}

}

class Test_21_A {

public static int val1;
public static int val2 = 1;

public static Test_21_A instance = new Test_21_A();

 Test_21_A() {
    val1++;
    val2++;
}

public static Test_21_A getInstance() {
    return instance;
}

}

  1. 1,1

代码顺序和定义的顺序保持一致

val2 被覆盖。

public class Test_22 {

public static void main(String[] args) {
    Test_22_A obj = Test_22_A.getInstance();

    System.out.println(Test_22_A.val1);
    System.out.println(Test_22_A.val2);
}

}

class Test_22_A {

public static int val1;

public static Test_22_A instance = new Test_22_A();

 Test_22_A() {
    val1++;
    val2++;
}

public static int val2 = 1;

public static Test_22_A getInstance() {
    return instance;
}

}

读取静态变量的底层实现

A Static Block A str

public class Test_1 { public static void main(String[] args) { System.out.printf(Test_1_B.str);

    while (true);
}

}

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"); } }

oop kclass 类的内存地址

InstanceClass Java类 (非数组)

InstanceMirrorClass 镜像类

ConstantPoolCache

str: jdk8: 静态属性存储在InstanceMirrorClass 镜像类

Test_1_B有没有str:

1 去Test_1_B的镜像类去取,如果有直接返回,如果没有 会沿着继承链请求向上。 算法的性能随着继承链death而上 算法复杂度O(n) super-class

2 借助另外的数据结构实现 使用k-v格式存储。查询功能O(1)

Hotspot使用第二种方式借助另外的数据结构ConstantPoolCache 常量池类ConstantPool中的属性_Cache指向这个结构。每一条数据对应一个类ConstantPoolCacheEntry

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

ConstantPoolCacheEntry 在哪???