JVM入门,认识Class文件!
JVM入门,认识Class文件
1、什么是JVM虚拟机
1.1概念
Java作为一门编程语言能够获得如此广泛的认可,除了它有结构严谨,面向对象的编程语言之外,它还具备一个非常突出的特性:一次编写
,到处运行
,即编写的程序可以摆脱硬件平台束缚,它提供了一种相对安全的内存管理和访问机制,避免了绝大部分内存泄漏和指针越界问题。
谈到jvm,就离不开与jdk和jre的对比,那么它们之间到底有什么区别和联系呢?
1.2 jdk/jre/jvm
我们先看这样一幅架构图,
从集合关系上看,jdk>jre>jvm
,除了范围上的区别,我们应该了解的是它们所包含的功能上的差别及各自发挥的作用。
-
jdk
jdk的全称是Java Development kit
(java开发工具包),我们可以把程序设计语言
、java虚拟机
、java类库
这三部分统称为jdk,jdk是用于支持java程序开发的最小环境
。Developer可以很容易的使用里面的方法以减少代码量,里面同时包含jre和一些开发的小工具(如编译工具javac),同时包含了jre。
-
jre
jre的全称是Java Running Environment
(java运行时环境 ),可以把java类库API中的javaSE的API子集
和java虚拟机
这两部分统称为JRE,JRE是支持java程序运行的标准环境
。
-
jvm
jvm的全称java virtual machine
(java 虚拟机),它只认识XXX.class文件
,虚拟机可以识别这种文件的字节码指令并调用操作系统上的API,正是这个原因,java才可以跨平台使用
。
1.3代码的执行
不管怎么说,jvm终究是一个软件
,那么它是怎样屏蔽底层的操作系统
、硬件
、CPU指令层
的细节呢?我们以Java程序为例来分析它的执行流程。
图中Test.java
文件是按照java语法规则编写的源文件,是一种高级语言,.java文件经javac编译后就生成字节码文件,字节码文件是用于给java虚拟机执行用的,该文件的格式规范受到java虚拟机的定义。而jvm的目的就是将字节码文件Test.class翻译
为操作系统及硬件的指令,便于在不同的操作系统上执行。
NOTE: jvm虚拟机并不是仅仅只针对java语言,像一些其它编程语言如Groovy
、Scala
和Kotlin
也可以在jvm虚拟机上运行上,这些语言仅仅需要实现一个编译器,通过该编译器把源代码文件编译成JVM能识别的字节码文件即可。
实现语言无关性的基础是虚拟机和字节码的存储格式,Java虚拟机不与包括Java语言在内的任何程序语言绑定,它只与Class文件这种特定的二进制文件格式所关联。
2、类文件结构
Class文件
是Java语言保持良好兼容性的关键,那么Class文件的结构是什么呢,存储那些内容呢?
事实上,Class文件是一组以8字节为基础单位的二进制流
,各个数据项目严格的按照顺序紧凑地排列在文件之中,中间没有添加任何分割符,这使得整个Class文件存储的内容几乎全部是程序运行的必要数据,没有空隙存在。
《Java虚拟机规范》规定了Class文件格式采用一种类似C语言结构体的伪结构来存储数据,这种伪结构只包含两种数据类型,即无符号数
和表
。
-
无符号数
无符号数属于基本
数据类型
,可以用来描述数字
、索引引用
、数量值
或按照UTF-8编码构成的字符串值
-
表
表是由多个无符号数或者其他表作为数据项构成的
复合数据类型
,为了便于区分,所有表的命名的都以_info
结尾。
class文件通过固定的数据结构排列顺序并且每种数据结构指定了占用的字节长度来紧凑的在组成了完整的可读文件,jvm只需要从文件开始的地方一步一步的读取能够完全的解析出这个类文件的内容。
来感受一下字节码文件长啥样!
懵逼了吧,这都是些啥玩意,但是理解这些十六进制数字对我们来说是非常有必要的,如果我们能充分理解每一个字节码文件的细节,自己就可以反编译出Java源文件。在下面的学习中,我们将一步步拆解class文件。
2.1魔数与版本
在class文件中,前4个字节被称为魔数
,它能够唯一确定class文件能否被虚拟机接受。其实,魔数还广泛应用在GIF、JPEG等文件头中。
紧接着魔数的4个字节存储的是Class文件的版本号
,第5和第6个字节是次版本号
,第7和第8个字节是主版本号
。Java的版本号是从45开始的,JDK1.1之后的每个JDK大版本发布的主版本号加1(JDK1.0~1.1使用了45.0~45.3的版本号),《Java虚拟机规范》在Class文件校验部分明确要求了即使文件格式并未发生变化,虚拟机也必须拒绝执行超过其版本号的Class文件,所以高版本的JDK能向下兼容以前版本的Class文件,但是不能运行以后版本的Class文件。
2.2常量池
在魔数、版本号之后,下一个位置存储的就是常量池
,常量池可以认为是Class文件里的资源仓库
,它是Class文件结构中与其它项目关联最多的数据。常量池的前两个字节占有的位置称为常量池计数器
(constant_pool_cont),它记录着常量池的组成元素常量池项
(cp_info)的个数。
常量池计数器是从1开始的,而不是从0开始的,即如果常量池计数器的值constant_pool_count=22
,则后面的cp_info的个数就为21,这是因为在指定class文件规范的时候,将第0项常量空出来是为了满足某些指向常量池的索引值的数据在特定的情况下表达”不引用任何一个常量池项
“,这种情况下可以将索引值设置为0来表示。
常量池中主要存放两大类常量:字面量
和符号引用
,字面量可以理解为Java语言层面上的的常量
概念,如文本字符串
、被声明为final
的常量值等。而符号引用则包括类和结构的全限定名称
、字段的名称和描述符
、方法的名称和描述符
等。
Class文件存储了方法
、字段
等各种类信息,但是它仅仅是存储了而已,它是不能反映出方法、字段等信息在内存中的布局。这是因为Java语言并不像C++语言有链接的概念,但是Java语言在虚拟机加载时会进行动态的连接
,虚拟机将会从常量池中获得对应的符号引用
,再在类创建时或运行时进行解析
、翻译
到具体的内存地址之中。
2.3访问标志
在常量池结束之后,紧接着的2个字节代表访问标志
(access_flags),这个标志用于识别一些类或者接口层次的访问信息
。比如标识一个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;是否被声明为final。具体的标志位可以如图
标志值与标志名称的对应关系如下:
标志值 | 标志名称 |
---|---|
0x0001 | ACC_PUBLIC |
0x0010 | ACC_FINAL |
0x0020 | ACC_SUPER |
0x0100 | ACC_INTERFACE |
0x0200 | ACC_ABSTRACT |
0x1000 | ACC_SYNTHETIC |
0x2000 | ACC_ANNOTATION |
0x4000 | ACC_ENUM |
标志名称就是限定访问信息的,如ACC_PUBLIC表示为是否为public类型,ACC_FINAL表示为是否被声明为final,其它的标志类似。
2.4索引
访问标志结束后,紧接就是索引
,包括类索引
、父类索引
与接口索引集合
,Class文件可以由这三项数据来确定该类型的继承关系。我们先了解下这三类索引的各有什么作用
-
类索引
类索引用于确定这个类的全限定名
,通过类的全限定名找到这个类,所以类索引的作用就是为找出class文件所描述的这个类叫什么名字。
-
父类索引
父类索引用于确定这个类的父类的全限定名
,有Java语言不支持多重继承,所以除了Object外,其它类的父类索引只有一个。
-
接口索引的集合
它是用来描述这个类实现哪些接口
,由于接口是多实现的,所以这些实现的接口将会按顺序排列在索引集合中。接口索引的集合在入口处会有一个计数器
,它用来表示集合中索引的数量
,如果该类没有实现接口,则该计数器为0。
Note:类索引、父类索引和接口索引集合指向常量池中的符号引用。
2.5字段表集合
字段表集合用于描述接口
或者类中声明的变量
,它有若干个字段表组成,字段表集合的就类似一个数组的结构,jvm在编译类的时候,会将类中的定义的字段的个数统计到字段计数器中,然后将每一个字段信息以结构的形式组成起来放在字段计数器之后。其结构如下图:
特别需要注意的是,这里的字段包括类变量
以及实例变量
,但是不包括方法内部的声明的局部变量。
我们在思考这样一个问题,字段表存储的是那些信息,这些信息是什么呢,事实上,字段表存储的就是字段信息,我们整理如下
-
修饰符(public、protected、private)
-
实例变量还是类变量(被static修饰)
-
可变性(final)
-
并发可见性(volatile)
-
是否可被序列化(transient)
-
字段数据类型(基本类型、对象、数组)
-
字段名称
既然字段有那么多信息,他的存储的形式是怎样的呢?事实上,字段的存储和我们写字段的形式是一样的,不懂?那我们就回顾下!
在字节码,JVM定义了filed_info结构体来描述字段,它的形式也很简单,就是一个结构体,
Field_info{ access_flags; name_index; descriptor_index; attribute_count; attributes; }
access_flags
是访问标志,与前面讲解的访问标志功能是类似的,紧接着access_flags标志的是name_index
和descriptor_index
,它们是对常量池的引用,分别代表着字段的简单名称以及字段和方法的描述符。简单名称就是指没有类型和参数修饰的方法或者字段名称;字段和方法描述符指的是基本类型的头一个大写字母,如基本数据类型是byte,则方法描述修饰符是B。attribute_count
表示的属性计数器,attributes
包含三部分内容(属性名称索引、属性的长度和常量值索引)
2.6方法表集合
方法表集合结构同字段表集合的结构是一样的,我们这里主要讲解它们之间的区别,剩下都可以按照属性表集合来学习。
-
区别一
对于方法来说,volatile关键字和transient关键字是不修饰方法的,所以访问标志中不会有相应的标志;但是synchronized、native、stricftp和abstract关键字是可以修饰方法的,所以在会有相应的访问标志。
-
区别二
与字段相比,方法内是有代码的,那么方法内的代码存储到哪里去了呢?事实上,对于方法里的Java代码,经Javac编译器编译成字节码的指令后,存放在方法属性表集合中会有一个名为Code的属性里面。
总结
Class文件的主要结构都说完了,我们从宏观的角度看看Class文件到底是什么样,话不多说,来看图
参考文献
周志华.深入理解JVM虚拟机
https://blog.csdn.net/luanlouis/article/details/41046443
我是Simon郎
,一个想要每天都要博学的小青年,喜欢的话可以关注
,转发
、点赞
,期待在未来的日子里你我共同进步!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
教你如何在Python中读,写和解析CSV文
摘要:在这篇文章中关于“在Python如何阅读CSV文件”中,我们将学习如何读,写和解析的CSV文件的Python。 您知道将表格数据存储到纯文本文件背后的机制是什么吗?答案是CSV(逗号分隔值)文件,该文件允许将数据转换为纯文本格式。在这篇文章中关于“在Python如何阅读CSV文件”中,我们将学习如何读,写和解析的CSV文件的Python。 将详细讨论以下方面: 什么是CSV文件及其用途? 为什么使用CSV文件格式? Python CSV模块 CSV模块功能 在Python中执行写入,读取CSV文件的操作 让我们开始吧。 什么是CSV文件及其用途? CSV(逗号分隔值)是一种纯文本文件格式,用于存储表格数据(例如电子表格或数据库)。它本质上存储的表格数据包括数字和纯文本。大多数在线服务使用户可以自由地将网站中的数据导出为CSV文件格式。CSV文件通常会在Excel中打开,几乎所有数据库都具有不同的特定工具以允许导入相同的文件。 文件的每一行都称为记录。每个记录由用逗号分隔的字段组成,这些字段也称为“定界符”,这是默认定界符,其他记录包括pipe(|),分号(;)。下面给出的是一个普...
- 下一篇
分享 10个我常逛的国外技术社区,真的受益匪浅!
本文收录在个人博客:www.chengxy-nds.top,技术资源共享,一起进步 经常有小伙伴问:“小富,有什么国外的技术社区、博客推荐吗?” 总有人问,索性我就把自己经常访问的10个国外技术社区分享出来。想要玩转这些资源的前提,要么自身外语水平不错,要么找个好的翻译工具,不然....。 不过,也不要一味的崇拜国外的技术,其实你看一圈下来国外社区不错的也就那几个,而国内的技术社区像掘金、思否也都是比较优秀的。当你在羡慕外边的景色时,殊不知别人也在羡慕着你。 授人以渔 1、dev dev社区和国内的掘金社区很相似,技术分类也比较多,像Java、Python、js、分布式等应有尽有,文章质量普遍都还不错,其实如果平时多留意不难发现,掘金上有一些文章是翻译自dev社区。 不过,在这还是要吹一波彩虹屁,无论界面布局还是文章质量,更喜欢掘金一点,难得的高质量技术社区。 地址:https://dev.to/ 2、stackoverflow stackoverflow 一个问答类的技术社区,和国内知乎比较相似,但与知乎不同的是stackoverflow 更垂直于技术,不像知乎内容比较杂。 地址:h...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Hadoop3单机部署,实现最简伪集群