1.JVM
执行引擎包含三部分:解释器,即时编译器,垃圾回收器
- 高级语言翻译为机器指令,主要是由执行引擎完成的。
- 解释器(解释运行,把字节码翻译为机器指令,主要负责翻编译器性能)。
- jit及时编译器(编译器后端,主要是把热点代码缓存起来,主要负责编译器性能)组成执行引擎。
- JVM 是 java虚拟机,是用来执行java字节码(二进制的形式)的虚拟计算机。
- jvm是运行在操作系统之上的,与硬件没有任何关系。
- 通过java指令 将字节码文件.class 进行解析,首先class文件先通过classloader加载到内存中,会用到一些java的相关类库,比如说object 或者String 等等。进行调用字节码的解释器JIT即时编译器,来对字节码文件进行编译,编译之后,再由执行引擎执行,执行引擎面对的是操作系统和硬件。我们可以把整个的java指令的这一部分流程称为jvm。
- JVM是一种规范的 定义了java虚拟机能够执行什么东西
2.跨平台的语言,跨语言的平台
跨平台的语言
- 只要可以生成jvm可以认识的字节码文件/.class文件,jvm就能认识。
- Java虚拟机根本不关心运行在其内部的程序到底是使用何种编程语言编写的,它只关心“字节码”文件。也就是说Java虚拟机拥有语言无关性,并不会单纯地与Java语言“终身绑定”,只要其他编程语言的编译结果满足并包含Java虚拟机的内部指令集、符号表以及其他的辅助信息,它就是一个有效的字节码文件,就能够被虚拟机所识别并装载运行。
跨语言的平台
字节码
- 我们平时说的java字节码,指的是用java语言编译成的字节码。准确的说任何能在jvm平台上执行的字节码格式都是一样的。所以应该统称为:jvm字节码。
- 不同的编译器,可以编译出相同的字节码文件,字节码文件也可以在不同的JVM上运行。
- Java虚拟机与Java语言并没有必然的联系,它只与特定的二进制文件格式——Class文件格式所关联,Class文件中包含了Java虚拟机指令集(或者称为字节码、Bytecodes)和符号表,还有一些其他辅助信息。
3.多语言混合编程
- Java平台上的多语言混合编程正成为主流,通过特定领域的语言去解决特定领域的问题是当前软件开发应对日趋复杂的项目需求的一个方向。
- 试想一下,在一个项目之中,并行处理用Clojure语言编写,展示层使用JRuby/Rails,中间层则是Java,每个应用层都将使用不同的编程语言来完成,而且,接口对每一层的开发者都是透明的,各种语言之间的交互不存在任何困难,就像使用自己语言的原生API一样方便,因为它们最终都运行在一个虚拟机之上。
- 对这些运行于Java虚拟机之上、Java之外的语言,来自系统级的、底层的支持正在迅速增强,以JSR-292为核心的一系列项目和功能改进(如DaVinci Machine项目、Nashorn引擎、InvokeDynamic指令、java.lang.invoke包等),推动Java虚拟机从"Java语言的虚拟机"向"多语言虚拟机"的方向发展。
4.常见虚拟机
所谓虚拟机(Virtual Machine),就是一台虚拟的计算机。它是一款软件,用来执行一系列虚拟计算机指令。大体上,虚拟机可以分为系统虚拟机和程序虚拟机。
- 大名鼎鼎的Virtual Box,VMware就属于系统虚拟机,它们完全是对物理计算机的仿真,提供了一个可运行完整操作系统的软件平台。
- 程序虚拟机的典型代表就是Java虚拟机,它专门为执行单个计算机程序而设计,在Java虚拟机中执行的指令我们称为Java字节码指令。
- 无论是系统虚拟机还是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中。
java虚拟机
- Java虚拟机是一台执行Java字节码的虚拟计算机,它拥有独立的运行机制,其运行的Java字节码也未必由Java语言编译而成。
- JVM平台的各种语言可以共享Java虚拟机带来的跨平台性、优秀的垃圾回器,以及可靠的即时编译器。
- Java技术的核心就是Java虚拟机(JVM,Java Virtual Machine),因为所有的Java程序都运行在Java虚拟机内部。
- Java虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对应平台上的机器指令执行。每一条Java指令,Java虚拟机规范中都有详细定义,如怎么取操作数,怎么处理操作数,处理结果放在哪里。
Sun HotSpot
-
提起HotSpot VM,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。
-
在2006年的JavaOne大会上,Sun公司宣布最终会把Java开源,并在随后的一年,陆续将JDK的各个部分(其中当然也包括了HotSpot VM)在GPL协议下公开了源码, 并在此基础上建立了OpenJDK。这样,HotSpot VM便成为了Sun JDK和OpenJDK两个实现极度接近的JDK项目的共同虚拟机。
-
在2008年和2009年,Oracle公司分别收购了BEA公司和Sun公司,这样Oracle就同时拥有了两款优秀的Java虚拟机:JRockit VM和HotSpot VM。 Oracle公司宣布在不久的将来(大约应在发布JDK 8的时候)会完成这两款虚拟机的整合工作,使之优势互补。 整合的方式大致上是在HotSpot的基础上,移植JRockit的优秀特性,譬如使用JRockit的垃圾回收器与MissionControl服务, 使用HotSpot的JIT编译器与混合的运行时系统。
-
从服务器,桌面,到移动端,嵌入式都有应用。
BEA JRocket
- 专注于服务端应用(JRockit内部不包含解析器实现,全部代码都靠即时编译器编译后执行)。
- Jrockit JVM 是世界上最快的jvm3. 2008年被oracle收购。
- 专注于服务器端的应用。
- 它不太关注程序的启动速度,因此JRockit内部不包含解析器的实现,全部代码都靠及时编译器编译之后执行。
iBM J9
-
市场定位与hotspot接近,服务器端,桌面应用,嵌入式等。
-
目前,是影响力最大的三大商业虚拟机之一。
-
应用于IBM的各种Java产品。
TaoBaoVM -hotspot深度定制版
- 淘宝定制款的JVM
LiquidVM 直接针对硬件 - 它是没有操作系统 没有Linux和unix 运行效率比较高
azul zing
- 最新垃圾回收的业界标杆 土豪版本 银行电信
- 它的垃圾回收号称1毫秒以内的
Microsoft VM - 一般大的厂商不会依赖oracle的东西 IBM 微软都有自己的平台,因为版权问题,所以不用
- 研发的自己的JVM
5.JDK、JRE、JVM
JDK
JDK是JRE的超集,JDK包含了JRE的所有开发,调试和监视应用程序的工具。当要开发Java应用程序时,需要安装JDK.
下面是JDK附带的一些重要组件:
-
apt 注解处理工具
-
javadoc 文档生成器,可以自动从源代码生成说明文档
-
jar 归档器,将相关的类库打包到一个JAR文件中。还可以帮助管理JAR文件
-
jConsole Java监控和管理平台
-
jhat Java堆分析工具
-
jstack 打印Java线程的堆栈信息
-
keytool 策略创建和管理工具
-
jarsigner Java签名和验证工具
-
Java运行时环境(JRE)是一个软件包,它捆绑了libraries(jar)和JVM,以及用Java编写的应用程序的其他组件。JVM只是JRE发行版的一部分。
JRE
执行任何Java程序,需要在机器上安装JRE,这是最低要求。
JRE捆绑的组件如下:
- DDL文件:由Java客户端虚拟机使用.
- 代码库,属性设置,资源文件也都包含,比如rt.jar和charsets.jar
- Java扩展文件,如localedata.jar
- 包含用于安全管理的文件,这些文件包括安全策略(security policy)和安全属性(security properties)
- 包含applets支持类的jar文件
- 包含平台使用的TrueType字体文件
JRE可以作为JDK的一部分下载或者也可以单独下载,JRE依赖于平台,这意味着根据机器类型(操作系统和体系结构),必须选择要导入和安装的JRE包
JVM
JVM(JAVA虚拟机)是运行Java字节码的虚拟机,通过编译.java文件为.class文件得到字节码文件 . .class文件包含JVM可以理解的字节码。
在现实世界中,JVM是一种规范,它提供可以执行Java字节码的运行时环境。不同的供应商提供这种规范的不同实现,
6.class文件
class file
文档地址
8个字节为基础单位的二进制文件字节流各项数据会严格的按照顺序紧凑的排列在class文件中,中间没有分隔符,使得class文件存储的内容几乎全部都是程序运行的。
Java虚拟机规范规定的,class文件格式采用的类似C语言的结构体的伪结构来存储的,这种结构只有两种数据类型。 无符号数 和 表
无符号数
无符号数:
属于基本数据类型 主要用于描述数字 索引符号 数量值 或者按照UTF-8编码构成的字符串值
数据类型 U1 U2 U4 U8 也只是逻辑上的区分。
u1 —表示一个字节
u2 —表示两个字节
U4 — 依次类推
u8 —依次类推
表
由多个无符号数或者其他表作为数据项构成的复合数据类型。所有的表都习惯以_info结尾 表主要用于描述有层次关系的复合结构数据。 比如 方法、字段 需要注意的是class文件没有分隔符,所以每个二进制数据类型都是严格定义的 具体的顺序如下:
clssfile结构体
jvm规范
/*
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
*/
type ClassFile struct {
magic uint32 // 魔数
minorVersion uint16 // 次版本号
majorVersion uint16 // 主版本号
constantPool ConstantPool // 常量池
accessFlags uint16 // 类访问标志
thisClass uint16 // 类常量池索引
superClass uint16 // 父类常量池索引(只有Object为0)
interfaces []uint16 // 接口常量池索引表
fields []*MemberInfo // 字段表
methods []*MemberInfo // 方法表
attributes []AttributeInfo // 属性表
}
魔数
文件格式必须以某个固定字节开头,判断文件的类型
class文件 0xCAFEBABE
PDF文件 %PDF
ZIP文件 PK
主次版本号
跟随jdk版本
说明1.2之前采用主次版本号,从1.2之后,次版本号为0
常量池
-
常量池的数量是不固定,所以在常量池的入口需要放置一个u2类型的数据,代表常量池的计数值CONSTANT_POOL_COUNT。
CONSTANT_POOL_COUNT 从1开始计数的。 class文件结构中只有常量池的容量计数是从1开始的。第0项腾出来满足后面某些指向常量池的索引值的数据,在特定的情况下需要表达“不引用任何一个常量池项目” 把索引值的第0项留给JVM自己用。
CONSTANT_POOL是没有索引值为0的入口的,但是在CONSTANT_POOL_COUNT缺失的第0项也是要被计算在内的。
比如CONSTANT_POOL 中有14项 那么CONSTANT_POOL_COUNT的数值就是15 -
常量池可以理解为Class文件之中的资源仓库,是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时也是在Class文件中第一个出现的表类型数据项目。
-
constant_pool_count:占2字节,0x0016,转化为十进制为22,即说明常量池中有21个常量(只有常量池的计数是从1开始的,其它集合类型均从0开始),索引值为1~22。第0项常量具有特殊意义,如果某些指向常量池索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义,这种情况可以将索引值置为0来表示
-
常量池中主要存放两大类常量:字面量和符号引用。字面量如文本字符串、声明为final的常量值等。符号引用包括三类常量:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
- Constant_pool[constant_pool_count-1]: 常量池, 常量池包含字符串常量、类名、接口名、字段名.
- Constant_pool是表结构,表结构(_info)有若干个项(item)构成。常量池中的项的结构:
cp_info{
u1 tag
u2 info[]
} //u:unsigned int - tag表如下:
每个常量的结构: - CONSTANT_Class_info{
u1 tag //tag=7
u2 name_index // name_index是索引值,指向CONSTANT_Utf8_info
} - CONSTANT_Fieldref_info{
u1 tag //9
u2 class_index //指向CONSTANT_Class_info;既可以表示类、也可以表示接口
u2 name_and_type_index //指向CONSTANT_NameAndType,表示字段名、字段描述符
} - CONSTANT_Methodref_info{
u1 tag //10
u2 class_index //指向CONSTANT_Class_info;表示类
u2 name_and_type_index //指向CONSTANT_NameAndType,表示方法名、方法描述符
} - CONSTANT_InterfaceMethodref_info{
u1 tag //11
u2 class_index //指向CONSTANT_Class_info;表示接口
u2 name_and_type_index //指向CONSTANT_NameAndType_info,表示方法名、方法描述符
} - CONSTANT_String_info{
u1 tag // 8
u2 string_index //指向CONSTANT_Utf8_info
} - CONSTANT_Integer_info{
u1 tag //3
u4 bytes //int 常量值
}
- CONSTANT_Float_info{
u1 tag //4
u4 bytes //float 常量值
} - CONSTANT_Long_info{
u1 tag //5
u4 high_bytes
u4 low_bytes // high_bytes、low_bytes共同表示8个字节的long常量
} - CONSTANT_Double_info{
u1 tag //6
u4 high_bytes
u4 low_bytes // high_bytes、low_bytes共同表示8个字节的long常量
}
- CONSTANT_NameAndType{
u1 tag //12
u2 name_index //指向CONSTANT_Utf8_info,表示名称
u2 descriptor_index //指向CONSTANT_Utf8_info,表示描述符
} - CONSTANT_Utf8_info{
u1 tag //1
u2 length
u1 bytes[length] //长度为length的字符串数组
} - CONSTANT_MethodHandle_info{
u1 tag // 15
u1 reference_kind //取值1~9,表示方法句柄类型
u2 reference_index //可以指向CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info
} - CONSTANT_MethodType_info{
u1 tag //16
u2 descriptor_index //指向CONSTANT_Utf8_info
} - CONSTANT_InvokeDynamic_info{
u1 tag //18
u2 bootstrap_method_attr_index //对bootstrap_methods数组的索引
u2 name_and_type_index //指向CONSTANT_NameAndType_info
}
字面量和符号引用
字面量:
int i = 1;把整数1赋值给int型变量i,整数1就是Java字面量,
String s = "abc";中的abc也是字面量。
符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。方法名,类名,字段名都是符号引用
直接引用(Direct References):直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那么引用的目标一定是已经存在于内存中。
access_flag
用于表示对该类或接口的访问权限以及该类或接口的属性
javap
javap -v 全路径(target下文件)
JClasslib
访问标志为什么是0x0021 这是一个按位与的运算,通过ACC_PUBLIC&ACC_SUPER这两个属性的按位与运算得来的值。使用2个字节来表示,可以代表很多内容。
总共有18个编号的常量类型。
编号1: CONSTANT_UTF8_INFO
TAG1 ------占用一个空间字节
Length: utf-8字符串占用的字节数
Bytes 长度为length字符串
用于表示utf-8的编码的字符串
编号3 CONSTANT_integer_info
Tag3
Bytes 4个字节 Big_Endian(高位在前) 存储int类型的值
编号4 CONSTANT_float_info
Tag4
Bytes 4个字节 Big_Endian(高位在前) 存储float类型的值
编号5 CONSTANT_long_info
Tag5
Bytes 8个字节 Big_Endian(高位在前) 存储long类型的值
编号6 CONSTANT_double_info
Tag6
Bytes 8个字节 Big_Endian(高位在前) 存储double类型的值
编号7 CONSTANT_Class_info
Tag7
Index 2个字节 指向类的全限定名的项的索引
类和接口符号引用
编号8 CONSTANT_String_info
Tag8
Index 2个字节 指向字符串的字面量的索引
编号9 CONSTANT_Field_info
Tag9
Index 2个字节 指向声明字段的类或接口的描述符 CONSTANT_Class_info的索引项
Index 2个字节 指向字段描述符CONSTANT_NameAndType的索引项
字段的符号引用
编号10 CONSTANT_Method_info
Tag10
Index 2个字节 指向声明字段的类或接口的描述符 CONSTANT_Class_info的索引项
Index 2个字节 指向字段描述符CONSTANT_NameAndType的索引项
类中方法的符号引用
编号11 CONSTANT_InterfaceMethodref_info
Tag11
Index 2个字节 指向声明字段的类或接口的描述符 CONSTANT_Class_info的索引项
Index 2个字节 指向字段描述符CONSTANT_NameAndType的索引项
接口中方法的符号引用
编号12 CONSTANT_NameAndType
Tag12
Index 2个字节 指向该字段或方法名称常量项的索引
Index 2个字节 指向该字段或方法描述符常量项的索引
字段或方法的符号引用
编号15 CONSTANT_MethodHandler_info
Tag15
Reference_kind 1个字节 1-9之间的一个值 决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为
Reference_index 2个字节 对常量池的有效索引。
表示方法句柄
编号16 CONSTANT_MethodType_info
Tag16
Descriptor_index 2个字节 指向UTF8_info 结构表示的方法描述符
编号18CONSTANT_InvokeDynamic_info
Tag18
Bootstrap_method_attr_index: 2个字节 当前class文件中引导方法表的bootstrap_methods[] 数组的有效索引
Name_and_type_index: 2个字节 指向NameAndType_info 表示方法名和方法描述符。
表示动态方法的调用点。
类索引、父类索引和接口索引集合
this_class
super_class
interface_count
Class文件中由这3项数据来确定这个类的继承关系
-
this_class:类索引,用于确定这个类的全限定名,占2字节
-
super_class:父类索引,用于确定这个类父类的全限定名(Java语言不允许多重继承,故父类索引只有一个。除了java.lang.Object类之外所有类都有父类,故除了java.lang.Object类之外,所有类该字段值都不为0),占2字节
-
interfaces_count:接口索引计数器,占2字节。如果该类没有实现任何接口,则该计数器值为0,并且后面的接口的索引集合将不占用任何字节,
-
interfaces:接口索引集合,一组u2类型数据的集合。用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句(如果该类本身为接口,则为extends语句)后的接口顺序从左至右排列在接口的索引集合中
-
this_class、super_class与interfaces中保存的索引值均指向常量池中一个CONSTANT_Class_info类型的常量,通过这个常量中保存的索引值可以找到定义在CONSTANT_Utf8_info类型的常量中的全限定名字符串
-
this_class的值为0x0001,即常量池中第一个常量,super_class的值为0x0003,即常量池中的第三个常量,interfaces_counts的值为0x0000,故接口索引集合大小为0
具体链接
常量池解析
当我们看到init字样的时候,是一个构造方法
当我们看到()V
- () 表示方法没有参数
- V 表示void 方法没有返回值。