浅谈JVM字节码和执行引擎

本文介绍了Java语言的“一次编写,到处运行”理念,详细阐述了Class文件结构,包括常量池、访问标志、继承关系和字节码指令。同时探讨了JVM的执行引擎,尤其是HotSpot中的解释执行和即时编译策略,以及热点代码的检测和优化。
摘要由CSDN通过智能技术生成

       Java诞生之时曾提出过一个非常著名的口号“一次编写,到处运行(Write Once,Run Anywhere)”。在竞争激烈的IT业界,各种不同的硬件体系结构、操作系统长期并存和发展。“与平台无关性”的理想最终只能实现在操作系统以上的应用层:Oracle公司以及其他虚拟机发行商发布过许多可以运行在各种不同硬件平台和操作系统上的JVM,这些JVM都可以载入和执行同一种平台无关的字节码,从而实现了程序的“一次编写,到处运行”。各种不同平台(可以理解为不同的指令集架构,意味着要设计对应的CPU和编译器)的JVM,以及所有平台都统一支持的程序存储格式(字节码)是构成平台无关性的基石。

        Java语言中的各种语法、关键字、常量、变量和运算符号的语义,最终都会由多条字节码指令组合来表达。就像C语言可以被编译程序翻译成多条汇编指令一样。

        Class文件是一组以八个字节为基础单位的二进制流,各个数据项严格按照顺序紧凑排列在文件中,中间没有任何分隔符。无论是顺序还是数量,甚至于数据存储的字节顺序,各种细节都被严格限定,哪个字节起始地址、代表什么含义、长度是多少,全部都不允许改变。具体来说,每个Class文件头4字节成为魔数,第5、6字节是次版本号,第7、8字节是主版本号,虚拟机必须拒绝执行超过其版本号的class文件。紧接着是常量池入口,通常是占用class文件空间最大的数据项之一。常量池的常量数量是不固定的,所以常量池入口需要有一个无符号数代表常量池容量计数值。常量池主要存放两大类常量:字面量和符号引用。主要包括:类和接口的全限定名,字段的名称和描述符、方法的名称和描述符、方法句柄和方法类型等。

       在常量池结束之后,紧接着的2个字节代表访问标志,用于识别一些类或者接口层次的访问信息,包括:这个class是类还是接口;是否定义为public类型;是否定义为abstract类型;是否被声明为final;等等。

        Class文件中有类索引、父类索引、接口索引集合这三项 来确定该类型的继承关系。类索引确定类的全限定名,父类索引确定类的父类的全限定名。接口索引集合用来描述这个类实现了哪些接口。

        接着是字段表集合、方法表集合、属性表集合。字段表用于描述接口或类中声明的变量,包括类变量和成员变量,但不包含在方法内部声明的局部变量。字段的访问修饰符、static修饰符、final修饰符、volatile修饰符、transient修饰符、字段数据类型、字段名称等都被记录着,上述信息中各个修饰符都是布尔值(要么有,要么没有)。方法表集合的结构如同字段表一样,依次包含访问标志、名称索引、描述符索引、属性表集合。

       最后,经过Javac编译后的字节码指令被存放在方法表中一个名为code的属性中。

        对于class文件结构的简单介绍到此为止,下面浅谈JVM的执行引擎。

        执行引擎是Java虚拟机核心的组成部分之一。物理机器的执行引擎是直接建立在处理器、指令集和操作系统层面上的,而虚拟机的执行引擎则是由软件自行实现。在不同的虚拟机实现中,执行引擎在执行字节码指令的时候(虚拟机执行引擎如何看待字节码文件可以类比操作系统如何看待可执行文件),通常会有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种选择,也可能两者兼备。

        所谓解释执行,就是翻译一条指令后立即执行,反复如此。编译则是将高级语言程序先全部翻译成目标机器指令相关的二进制代码,而即时编译是运行期间把字节码翻译成本地机器码。

       在HotSpot中默认两者兼备,既可以解释执行字节码,也会将热点代码即时编译成机器码,这样热点代码的速度更快,效率则更高。

        Java程序最初都是通过解释器进行解释执行的,当虚拟机发现某个方法或者代码块的运行特别频繁时,就会把这些代码认定为“热点代码”,为提高热点代码的执行效率,在运行时虚拟机将会把这些代码编译为本地机器码,运行时完成这个任务的后端编译器被称为即时编译器。

        热点代码主要包括两类:被多次调用的方法、被多次执行的循环体。对于这两种情况,编译的目标对象都是整个方法体,而不会是单独的循环体。JVM会用某些算法来对热点进行探测。

        即时编译虽然相对而言运行速度快,但只采用即时编译也存在缺点。首先,JVM无法获得程序的运行时信息,那么JVM无法对代码进行优化,而如果先进行解释执行,JVM就能够了解程序运行时的情况,从而进行优化,且不仅仅是把热点代码即时编译为机器码;其次,编译得到的机器码需存储到磁盘上,如果全部都提前编译就需要占用比较大的存储空间,把热点代码即时编译只需要存储热点代码的机器码。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值