JVM底层之类加载
安装 Ubuntu
HSDB
源码工具 Source Insight
IDEA插件 jclasslib
深入理解Java虚拟机 书。
jsp-l 查询进程ID
kclass模型
Java的每个类,在JVM中,都有一个对应的Klass类实例与之对应,存储类的元信息如:常量池、属性信息、方法信息……
看下klass模型类的继承结构
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的存在形式
类的加载过程
加载
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、符号引用验证
准备
赋初值
!!!!! final修饰 直接赋值 没有赋初值
解析
间接引用转直接引用
间接引用 指向运行时常量池的引用 直接引用 内存地址
常量池 class文件的常量池(静态) 运行时常量池(HSDB) 字符串常量池 StringTable
初始化
执行静态代码段
1 定义一个static JVM自动生成clinit
2 生成的clinit方法
代码顺序和定义的顺序保持一致
题目:
- 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
代码顺序和定义的顺序保持一致
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 在哪???