虚拟机
1.1 倒退历程
1.1.1 java往事
Java诞生在一群懈怠、浮躁而高傲的程序蠢才之中。
1990年12月,Sun的工程师Patrick Naughton被过后蹩脚的Sun C++工具折磨的快疯了。他大声埋怨,并威逼要来到Sun转投过后在Steve Jobs领导之下的NeXT公司。领导层为了留住他,给他一个机会,启动了一个叫做Stealth(秘密行动)的我的项目。
随着James Gosling等人的退出,这个我的项目更名为Green。其指标是应用C++为嵌入式设施开发一种新的根底平台技术,James Gosling自己负责开发一个编辑器。正如人们预先剖析的那样,这位蠢才的程序员太懈怠,所以没有把C++学好,开发中碰了一头包。于是他决定开发一种新的编程语言。他把这种语言命名为C++++--,意思是C++ “加上一些好货色,减去一些坏东西”。显然这个蹩脚的名字不可能短暂,于是很快这种颇受伙伴青睐的小语言被命名为Oak。
到了1992年9月,Oak语言连同Green OS和一些应用程序一起公布在称做Start 7的小设施上,有了第一次精彩的亮相。随后,Sun开了一家名为FirstPerson的公司,整个团队被转移到这家公司里研发机顶盒,以招标时代华纳公司的一个我的项目。这帮蠢才被技术狂热所鼓励,开发出了一个高交互性的设施,后果没想到时代华纳公司和有线电视服务商并不违心用户领有那么大的控制权,从而在竞标之战中败给了SGI。
Sun无奈地敞开了FirstPerson,召回了整个团队,java的前途却没有因而而葬送,随着互联网倒退的涌动,java开始来到嵌入式小设施,往互联网歪斜。1994年,Oak被命名为Java,回到了激情磅礴的IT产业,抓住互联网的大潮,从此一发不可收拾。
剩下的事件,大家都晓得了……
1.1.2 版本迭代
- 1991 年,James Gosling 博士公布产品 Oak( 橡树),这是 Java 语言的前身。
- 1995 年,Oak 语言改名为 Java。
- 1996 年,JDK(Java开发所应用的工具包)1.0 公布,提供了纯解释执行的 Java 虚拟机实现:Sun Classic VM。
- 1997 年,JDK1.1 公布,代表技术有:JDBC、JavaBeans、外部类、反射。
1998 年,JDK1.2 公布,Java 技术体系被拆分为 J2SE、J2EE、J2ME 三大体系。
- 2000 年,JDK1.3 公布,默认的 Java 虚拟机由 Sun Classic VM 改为 HotSopt。
- 2002 年,JDK1.4 公布,Java 真正走向成熟,代表技术有:正则表达式、NIO等。
- 2004 年,JDK5.0 公布,对语法易用性做了很大改良,新增了泛型、枚举等,代表技术有:并发包等。
- 2006 年,JDK6.0 公布,将 J2EE/J2SE/J2ME 的命名形式改为 Java SE 6、Java EE 6、Java ME 6。
- 2009 年,Sun 公司因为经营不善被 Oracle 公司收买。
- 2011 年,JDK7 公布。
- 2013 年,JDK8(LTS) 公布,函数式编程,lamda表达式。
- 2017年,JDK9
- 2018年,JDK 10,11(LTS)正式公布
- 2019年,JDK 12,13
- 2020年,JDK 14,15
- 2021年,JDK 16,17(LTS)
附:sun与微软的轶事java诞生的1995年,正是微软在软件产业位置达到巅峰的时代。然而这个老成持重的毛头小子硬是引起了微软帝国的关注。所以96年微软就向sun申请了java认证。微软的加持的确推动了人们对java的信念和趣味。然而好景不长,从1997年公布Visual J++的第一个版本开始,微软就开始在Java中掺入本人的公有扩大。这毫无疑问引起Sun的高度重视。1997年10月,Sun向美国加州地方法院起诉微软公司违反两公司就微软应用Java技术所签定的合同,指控微软公司在本人的Java产品中做了“不失当的批改”,违反了合同中承诺向用户提供Java兼容产品的条款。这一官司始终打到了2001年1月单方达成和解。到了2001年7月,微软颁布新版的Windows XP将不再反对Sun的JVM,并且推出了.NET平台与Java分庭抗礼。当然目前.net用的人少了,这是后话。
1.1.3 两种jdk
openjdk vs oraclejdk:
- Oracle JDK将更多地关注稳定性,它器重更多的企业级用户,而OpenJDK常常公布以反对其余个性,不太稳固。
- Oracle JDK反对长期公布的更改(LTS),而Open JDK仅反对打算和实现下一个发行版。
- Oracle JDK依据二进制代码许可协定取得许可,而OpenJDK依据GPL v2许可取得许可。
- 2019年1月之后公布的Oracle Java SE 8的公开更新将无奈用于商业,然而,OpenJDK是齐全开源的,能够自在应用。
- Oracle JDK的构建过程基于OpenJDK,因而OpenJDK与Oracle JDK之间没有技术差别。
- 顶级公司正在应用Oracle JDK,Open JDK不太受欢迎。
- Oracle JDK具备良好的GC选项和更好的渲染器,而OpenJDK具备更少的GC选项
- 在响应性和JVM性能方面,Oracle JDK提供了更好的性能。
- Oracle JDK在运行JDK时不会产生任何问题,而OpenJDK有时会产生一些问题。
- Oracle JDK将从其10.0.X版本将免费,用户必须付费或必须依赖OpenJDK能力应用其收费版本。
- Oracle JDK齐全由Oracle公司开发,而Open JDK我的项目由IBM,Apple,SAP AG,Redhat等顶级公司退出和单干。
1.2 JVM体系
- JDK(Java Development Kit)是 Java语言的软件开发工具包,也是整个java开发的外围,它蕴含了JRE和开发工具包
- JRE(Java Runtime Environment),Java运行环境,蕴含了JVM和Java的外围类库(Java API)
- JVM(Java Virtual Machine),Java虚拟机,它是运行在操作系统之上的,它与硬件没有间接的交互
所谓“一次编码,随处运行“正是基于不同零碎下的jvm帮你覆盖了零碎之间接口的差别:
总结
jdk是开发人员的工具包,它蕴含了java的运行环境和虚拟机,而一次编写到处运行就是基于jvm
1.3 各种虚拟机
1.3.1 清单
1、Sun Classic VM
世界上第一款商用 Java 虚拟机。
1996年随着Java1.0的公布而公布,JDK1.4时齐全被淘汰
2、BEA JRockit
专一于服务端利用,号称是世界上最快的JVM
起初被 Oracle收买;Oracle JRockit (原来的 Bea JRockit)
3、IBM公司的 J9VM
全称:IBM Technology for Java Virtual Machine,简称IT4J,外部代号:J9
是 IBM 本人开发的一款 JVM
市场定位于HotSpot靠近,服务器端、桌面利用、嵌入式等多用途VM
4、HotSpot VM(当初最罕用)
它是Sun JDK和OpenJDK中所带的虚拟机,也是目前应用范畴最广的Java虚拟机。
5、其余
(TaobaoJVM 、Graal VM、Azul VM、Liquid VM、Apache Harmony、)虚拟机
1.3.2 查看
shawn@macpro:~ > java -versionjava version "1.8.0_181"Java(TM) SE Runtime Environment (build 1.8.0_181-b13)Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
- hotspot虚拟机
- Client VM是专门为疾速启动和小内存(small footprints)而优化的,像GUI就很适宜
- Server VM是专门为高性能利用而优化的,如服务器利用
- 版本是基于tag为1.8.0_181
1.4 jvm整体架构
1.4.1 java运行过程
1.源码编译:通过Java源码编译器将Java代码编译成JVM字节码(.class文件)
2.类加载:通过ClassLoader及其子类来实现JVM的类加载
3.类执行:字节码被装入内存,进入JVM虚拟机,被解释器解释执行
1.4.2 jvm模型
由下面的图能够看出,JVM虚拟机中次要是由三局部形成,别离是类加载子系统、运行时数据区、执行引擎。
类加载子系统
Java虚拟机把形容类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终造成能够被虚拟机间接应用的Java类型。
运行时数据区
Java虚拟机在执行Java程序的过程中会把它所治理的内存划分为若干个不同的数据区域。
这些区域有各自的用处,以及创立和销毁的工夫,有的区域随着虚拟机过程的启动而始终存在,有些区域则是依赖用户线程的启动和完结而建设和销毁。
执行引擎
执行引擎用于执行JVM字节码指令,次要有两种形式,别离是解释执行和编译执行,区别在于,解释执行是在执行时翻译成虚拟机指令执行,而编译执行是在执行之前先进行编译再执行。
解释执行启动快,执行效率低。编译执行,启动慢,执行效率高。
垃圾回收器就是主动治理运行数据区的内存,将无用的内存占用进行革除,开释内存资源。
本地办法库、本地库接口
在jdk的底层中,有一些实现是须要调用本地办法实现的(应用c或c++写的办法),就是通过本地库接口调用实现的。比方:System.currentTimeMillis()办法。
2、类文件构造
理解jvm后续的所有动作,先从字节码开始。它是所有产生的源头。
2.1 测试案例
2.1.1 源代码
package com.itheima.jvm.demo;public class ClassStruct { private static String name = "JVM"; public static void main(String[] args) { System.out.println("Hello " + name); }}
2.1.2 编译
1)maven定义编译的版本
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
2)编译
mvn clean compile
2.2 字节码构造
2.2.1 二进制概览
1)vscode关上
2)class文件是一个二进制文件,转化后是16进制展现,实际上class文件就是一张表,它由以下数据项形成,这些数据项从头到尾严格依照以下顺序排列:
类型 | 名称 | 数量 | 形容 |
---|---|---|---|
u4 | magic | 1 | 魔数 |
u2 | minor_version | 1 | 次版本号 |
u2 | major_version | 1 | 主版本号 |
u2 | constant_pool_count | 1 | 常量个数 |
cp_info | constant_pool | constant_pool_count - 1 | 具体常量 |
u2 | access_flags | 1 | 拜访标记 |
u2 | this_class | 1 | 类索引 |
u2 | super_class | 1 | 父类索引 |
u2 | interfaces_count | 1 | 接口索引 |
u2 | interfaces | interfaces_count | 具体接口 |
u2 | fields_count | 1 | 字段个数 |
field_info | fields | fields_count | 具体字段 |
u2 | methods_count | 1 | 办法个数 |
method_info | methods | methods_count | 具体方法 |
u2 | attributes_count | 1 | 属性个数 |
attribute_info | attributes | attributes_count | 具体属性 |
3)图示如下:
2.2.2 魔数与版本
1)魔数:
CAFEBABE,咖啡宝宝,固定的。
2)版本号:
34,换成10进制就是52
jdk的版本标记映射关系:
阐明编译用的是jdk8,咱们改成1.6,从新执行 mvn clean compile ,再来查看class文件试试:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build>
扩大
在开发中,常常会遇到相似Unsupported major.minor version 51.0的谬误,个别状况下都是JDK版本不匹配造成的。
尽管jdk代码在执行时基本上向下兼容,然而!开发环境和服务器环境jdk最好统一,不要尝试这个坑。
辨别和了解两个环境:编译环境,运行环境
2.2.3 常量池
再往下听从雷同的法则: 计数器(标注前面有多少个) + 对应个数的构造体
咱们以常量池为例:
1)地位
2)构造阐明
常量池记录了jvm内的一堆常量信息,这部分由 【2个字节计数】 + 【n个cp_info构造】组成
其中cp_info有多种类型:
- 间接类型,存的就是以后值,这种像Integer,Long等长度都是确定的
- 援用类型,存的是指向其余地位的指针
附:绿色代表指针,橙色代表间接类型
3)案例
上面以String为例,String是一种援用类,它会指向一个utf8类型来存储实在的信息
jdk提供了一个工具,javap,能够查看常量列表的具体内容:
javap -v ClassStruct.class
2.2.4 其余信息
1)阐明
常量池之后,是紧挨的一系列信息,这些信息大同小异,无非就是值、或者援用
(参考下面2.3.3里的表格和图例)
- 拜访标记:public abstract 等信息
- 类索引,class类型,最终指向一个utf8,标记以后类的名字
- 父类,同上
- 接口,2字节记录数量,前面记录多个接口类型
- 接下来是字段、办法、属性,都是2字节记录前面多少个,前面紧跟对应的构造体类型
2)注意事项
要看懂javap后的格局,明确这些格局,能够轻松看懂class构造
类型 | 标识符 | 案例 | 阐明 |
---|---|---|---|
数组 | [ | [Ljava.lang.String | String数组 |
对象 | L | Lcom.test.Demo | |
根本类型 | 大写字母结尾 | B=byte,I=int…… | |
组合类型
类型 | 案例 | 阐明 |
---|---|---|
类里的属性、字段、办法等 | com.test.Demo.name:Ljava.lang.String | 英文点号隔开 |
标识什么类型 | com.test.Demo.getName:()Ljava.lang.String | 英文冒号隔开 |
办法 | (参数类型)返回值类型 | 英文括弧,前面是返回值类型 |
3)实例剖析
如果本文对您有帮忙,欢送
关注
和点赞
`,您的反对是我保持创作的能源。转载请注明出处!