JVM虚拟机(整体架构类文件结构)我来了~~~

Posted 博学谷狂野架构师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM虚拟机(整体架构类文件结构)我来了~~~相关的知识,希望对你有一定的参考价值。

虚拟机

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:

  1. Oracle JDK将更多地关注稳定性,它重视更多的企业级用户,而OpenJDK经常发布以支持其他特性,不太稳定。
  2. Oracle JDK支持长期发布的更改(LTS),而Open JDK仅支持计划和完成下一个发行版。
  3. Oracle JDK根据二进制代码许可协议获得许可,而OpenJDK根据GPL v2许可获得许可。
  4. 2019年1月之后发布的Oracle Java SE 8的公开更新将无法用于商业,但是,OpenJDK是完全开源的,可以自由使用。
  5. Oracle JDK的构建过程基于OpenJDK,因此OpenJDK与Oracle JDK之间没有技术差异。
  6. 顶级公司正在使用Oracle JDK,Open JDK不太受欢迎。
  7. Oracle JDK具有良好的GC选项和更好的渲染器,而OpenJDK具有更少的GC选项
  8. 在响应性和JVM性能方面,Oracle JDK提供了更好的性能。
  9. Oracle JDK在运行JDK时不会产生任何问题,而OpenJDK有时会产生一些问题。
  10. Oracle JDK将从其10.0.X版本将收费,用户必须付费或必须依赖OpenJDK才能使用其免费版本。
  11. 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 -version
java 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文件就是一张表,它由以下数据项构成,这些数据项从头到尾严格按照以下顺序排列:

类型名称数量描述
u4magic1魔数
u2minor_version1次版本号
u2major_version1主版本号
u2constant_pool_count1常量个数
cp_infoconstant_poolconstant_pool_count - 1具体常量
u2access_flags1访问标志
u2this_class1类索引
u2super_class1父类索引
u2interfaces_count1接口索引
u2interfacesinterfaces_count具体接口
u2fields_count1字段个数
field_infofieldsfields_count具体字段
u2methods_count1方法个数
method_infomethodsmethods_count具体方法
u2attributes_count1属性个数
attribute_infoattributesattributes_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.StringString数组
对象LLcom.test.Demo
基本类型大写字母开头B=byte,I=int……

组合类型

类型案例说明
类里的属性、字段、方法等com.test.Demo.name:Ljava.lang.String英文点号隔开
标识什么类型com.test.Demo.getName:()Ljava.lang.String英文冒号隔开
方法(参数类型)返回值类型英文括弧,后面是返回值类型

3)实例分析

本文由传智教育博学谷教研团队发布。

如果本文对您有帮助,欢迎关注点赞;如果您有任何建议也可留言评论私信,您的支持是我坚持创作的动力。

转载请注明出处!

jvm整体结构

------------恢复内容开始------------

JVM架构模型

一、Jvm简图

技术图片

 这张图是针对当前最流行的HotSpot VM虚拟机来说。

其中名称以及作用:

1.classFiles:字节码文件。

2.类装载器子系统(加载器):将class文件加载为一个类。

3.其中方法区和堆是线程共有的,而java栈、本地方法栈、程序计数器是线程独有的。

4.程序计数器:程序计数器是一块较小的内存空间,可以把它当做当前线程所执行的字节码的行号指示器。程序的分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖程序计数器完成。

5.Java栈(Java虚拟机栈):Java栈描述的是Java方法执行的线程内存模型:每个方法执行的时候,Java虚拟机都会同步创建一个栈帧(栈帧是方法运行使其很重要的基础数据结构)用于存储局部变量表、操作数栈、动态连接、方法出口  等信息。每一个方法被调用直到执行完毕的过程,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

6.本地方法栈:本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。

7.堆(Java堆):Java堆是虚拟机所管理的内存中最大的一块内存空间。此内存区域的唯一目的就是存放对象实例。Java堆是垃圾收集器管理的内存区域,因此也被称为“GC堆”。

  如果从内存分配的角度来看,所有线程共享的Java堆中可以划分多个线程私有的分配缓冲区,可以提升对象分配时的效率,将Java堆细分的目的是为了更好的回收内存,或者更快的分配内存。

8.方法区:与Java堆一样,是多个线程共享的内存区域,用于存储已被虚拟机加载的类型信息、常量、静态常量、即时编译器编译后的代码缓存等数据。在jdk6的时候Hotspot开发团队就有了放弃永久代,逐步改用本地内存来实现方法区,  到了jdk8完全废弃了永久代的概念,改用在本地内存中实现元空间来代替。

9.运行时常量池:是方法区的一部分,用于存放编译时期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

10.执行引擎:将字节码编译成机器码(即汇编语言)交由操作系统完成。

二、指令架构

java编译器输入指令流是基于栈的指令集架构,另外一种指令架构基于寄存器指令集架构

两种指令集架构特点:

基于栈的指令集架构:

  1.设计和实现更简单,适用于资源受限的系统;(栈的资源有限)

  2.避开了寄存器的分配难题:使用的是零地址指令分配方式(零地址指令:只有操作数,没有地址指向操作数)

  3.指令流中大部分指令是零地址指令,执行过程依赖于栈操作。

    优点:指令集合小,编译器容易实现

    缺点:完成一项操作需要更多的指令完成(栈的原因导致),效率低

  4.不需要硬件的支持,可移植性好,利于跨平台(没有和硬件挂钩)

基于寄存器指令集架构特点:

  1.典型应用:传统pc以及android的Davlik虚拟机

  2.指令集架构依赖于硬件,可移植性差

  3.性能优秀,执行效率高(因为花费更少的指令去执行一个操作)

  4.大部分情况下,寄存器指令集往往都是一地址指令、二地址指令和三地址指令为主(指令集合大)

以上是关于JVM虚拟机(整体架构类文件结构)我来了~~~的主要内容,如果未能解决你的问题,请参考以下文章

jvm整体结构

Java虚拟机——JVM

JVM学习—整体介绍

JVM——Java虚拟机架构

01-JVM 与 Java 体系结构

JVM整体架构,你了解吗