JVM介绍
什么是JVM ?
一个Java虚拟机;
把Java源码编译成字节码进行运行;
Class文件解析
编译java源文件为class文件:javac User.java->User.class
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html
Class文件格式
Class文件遵循下面结构:
分析Class文件
(1)cafe babe:这是固定的class文件内容开头,代表这是一个class文件
(2)0000 0034:minor_version+major_version,16进制的34等于10进制的52,表示JDK的版本为8
(3)0043:constant_pool_count
The value of the constant_pool_count item is equal to the number of entries in the constant_pool table plus one
16进制的43等于10进制的67,表示常量池中常量的数量是66
(4)cp_info:constant_pool[constant_pool_count-1]
The constant_pool is a table of structures representing various string constants, class and interface names, field names, and other constants that are referred to within the ClassFile structure and its substructures. The format of each constant_pool table entry is indicated by its first “tag” byte.
The constant_pool table is indexed from 1 to constant_pool_count ‐ 1.|
字面量:文本字符串,final修饰的常量等
符号引用:类和接口的全限定名、字段名称和描述符、方法名称和描述符
(5)The constant pool
官网: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4
All constant_pool table entries have the following general format:
Table 4.4-A. Constant pool tags
Constant Type | Value |
---|---|
CONSTANT_Class |
7 |
CONSTANT_Fieldref |
9 |
CONSTANT_Methodref |
10 |
CONSTANT_InterfaceMethodref |
11 |
CONSTANT_String |
8 |
CONSTANT_Integer |
3 |
CONSTANT_Float |
4 |
CONSTANT_Long |
5 |
CONSTANT_Double |
6 |
CONSTANT_NameAndType |
12 |
CONSTANT_Utf8 |
1 |
CONSTANT_MethodHandle |
15 |
CONSTANT_MethodType |
16 |
CONSTANT_InvokeDynamic |
18 |
(6)u1 tag
由0a可以知道第一个常量的类型对应的10进制为10,对应上表可知这是一个方法引用CONSTANT_Methodref
,查找方法引用对应的 structure
0010对应class_index的十进制为16,0023对应name_and_type_index的十进制为35
(7)u1 info[]
由08得知十进制为8,对应上表可知是String引用CONSTANT_String
,查找对应的 structure
u2 string_index是0024对应十进制是36
反汇编
通过 dk 自带命令 javap -h可以对class文件进行反编译
javap ‐v ‐c ‐p User.class > User.txt 进行反编译,查看字节码信息和指令等信息
JVM相对class文件来说可以理解为是操作系统;class文件相对JVM来说可以理解为是汇编语言或者机 器语言。
类加载机制
官网:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html
Loading, Linking, and Initializing, 装载,链接,初始化
Loading
找到Class文件所在的全路径,然后加载到内存中;这个过程使用了类加载器ClassLoader;因为有很多类需要加载,所以用到很多类加载器:大体分为四种:
如果在两个不同的jar中出现两个全路径名字完全相同的类,那么同一个java进程由哪个类加载器去执行呢?为了解决这一问题,所以这里使用双亲委派,每次子类会先递归找到父类,如果父类有就不要子类加载器加载,如果没有,最后再逐级找到子类加载器加载;但是这样就会浪费时间。
所以如果我们确定某个类的全路径独一无二,其他地方不存在,那么我们自定义类加载器去加载,不需要使用双亲委派机制,tomcat就使用了这种自定义类加载器:
class MyClassLoader extends ClassLoader {
loadClass() {
// 自定义类加载器加载方法,打破双亲委派模型
}
}
打破双亲委派模型的方式:
- (1)重写loadClass方法
- (2)SPI机制
- (3)OSGi
Linking
(1)Verification:保证被加载类的正确性
(2)Preparation:为类的静态变量分配内存,并将其初始化为默认值
(3)Resolution:动态地将运行时常量池中的符号引用转换为直接引用
常量池:class文件中的constant_pool,保存在磁盘上;
运行时常量池:是class文件中的constant_pool运行时的表示,保存在内存中;
符号引用:class文件中的十六进制内容;
直接引用:十六进制对应的某个变量在物理内存中的某个内存地址;
Initializing
对类的静态变量,静态代码块执行初始化赋值操作
JVM运行时数据区
一些数据区随着JVM进程的创建而创建、退出而销毁;其他数据区随着线程的创建而创建、退出而销毁;
进程生命周期:方法区、堆
线程生命周期:Java虚拟机栈、本地方法栈、PC寄存器
方法区
方法区是所有jvm线程共享的内存区域;在jvm启动时被创建;存储每一个类的结构信息,例如运行时常量池、字段、静态变量、方法数据、构造器;
当方法区无法满足内存分配需求时,会抛出OutOfMemoryError异常。
堆
堆是所有jvm线程共享的内存区域;在jvm启动时被创建;堆是jvm所管理内存中最大的一块;java对象实例以及数组都在堆上分配;
堆内存空间不足时,也会抛出OutOfMemoryError异常。