JVM 内部原理(五)— 基本概念之 Java 虚拟机官方规范文档,第 7 版
2016-12-19 09:46
567 查看
JVM 内部原理(五)— 基本概念之 Java 虚拟机官方规范文档,第 7 版
介绍
版本:Java SE 7每位使用 Java 的程序员都知道 Java 字节码在 Java 运行时(JRE - Java Runtime Environment)里运行。Java 虚拟机(JVM - Java Virtual Machine)是 Java 运行时(JRE)的重要组成部分,它可以分析和执行 Java 字节码。Java 程序员不需要知道 JVM 是如何工作的。有很多应用程序和应用程序库都已开发完成,但是它们并不需要开发者对 JVM 有深入的理解。但是,如果你理解 JVM ,那么就可以对 Java 更有了解,这也使得那些看似简单而又难以解决的问题得以解决。
在本篇文章中,我会解释 JVM 是如何工作的,它的结构如何,字节码是如何执行的及其执行顺序,与一些常见的错误及其解决方案,还有 Java 7 的新特性。
目录
虚拟机(Virtual Machine)Java 字节码
症状
原因
类文件格式(Class File Format)
症状
原因
JVM 结构
类装载器(Class Loader)
运行时数据区
执行引擎
Java 虚拟机官方规范文档,第 7 版
分支语句中的字符串
总结
内容
Java 虚拟机官方规范文档,第 7 版
在 2011 年 7 月 28 日,Oracle 发布了 Java SE 7 并更新了 JVM 官方规范文档至 Java SE 7 的版本。在 1999 年发布《Java 虚拟机官方规范文档,第二版》后,Oracle 花了 12 年时间做这版更新。更新版本的内容包括这 12 年来积累的各种变更修改,规范文档的描述更为清晰。除此之外,它还反映了《Java 语言规范文档,第七版》的内容。主要的更新概括如下:Java SE 5.0 引入泛型,支持方法的参数变量。
字节码验证过程的技术从 Java SE 6 开始发生变化。
增加 invokedynamic 指令以及相关的类文件格式支持动态类型语言。
删除了对于 Java 语言本身的概念性描述,并将其归入《Java 语言规范文档》中。
删除了关于 Java 线程和锁的描述,并将其写入《Java 语言规范文档》。
最大的改变要数增加 invokedynamic 指令。这也意味着 JVM 内部指令集发生了变化,也就是说 JVM 从 Java SE 7 开始支持类型非固定的动态类型语言,如脚本语言,以及动态的 Java 语言。之前没有使用的操作码(OpCode)186 被应用到新指令 invokedynamic 以及新的类文件格式中以支持动态性,
由 Java 编译器 Java SE 7 创建的类文件版本是 51.0 。Java SE 6 的版本是 50.0 。类文件格式发生了很大变化,因此 51.0 版本的类文件不能运行于 Java SE 6 的 JVM 。
尽管有如此之多的变化,Java 方法的 65535 字节长度限制并没有被移除。除非 JVM 类文件格式发生了创新式的变化,否则它也不太可能在将来移除。
Oracle Java SE 7 VM 支持 G1 ,这个新的垃圾回收机制;不过,它仅限于 Oracle JVM ,所以 JVM 本身并不受限于任何垃圾回收机制。因此,JVM 官方规范文档并没有对此进行描述。
分支语句中的字符串
Java SE 7 增加了多种语法和特性。不过,与 Java SE 7 中语言发生的许多变化相比,JVM 并没有发生很多变化。那么,Java SE 7 的新特性是如何实现的呢?我们通过反编译看看 String 在分支语句中(一个将字符串传入 switch() 语句进行比较的功能)的实现方式。有如下代码:
// SwitchTest public class SwitchTest { public int doSwitch(String str) { switch (str) { case "abc": return 1; case "123": return 2; default: return 0; } } }
因为它是 Java SE 7 的新功能,所以它不能使用 Java SE 6 或更低版本的编译器来编译。用 Java SE 7 的 javac 编译它。用 javap -c 显示编译结果:
C:Test>javap -c SwitchTest.classCompiled from "SwitchTest.java" public class SwitchTest { public SwitchTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public int doSwitch(java.lang.String); Code: 0: aload_1 1: astore_2 2: iconst_m1 3: istore_3 4: aload_2 5: invokevirtual #2 // Method java/lang/String.hashCode:()I 8: lookupswitch { // 2 48690: 50 96354: 36 default: 61 } 36: aload_2 37: ldc #3 // String abc 39: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 42: ifeq 61 45: iconst_0 46: istore_3 47: goto 61 50: aload_2 51: ldc #5 // String 123 53: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 56: ifeq 61 59: iconst_1 60: istore_3 61: iload_3 62: lookupswitch { // 2 0: 88 1: 90 default: 92 } 88: iconst_1 89: ireturn 90: iconst_2 91: ireturn 92: iconst_0 93: ireturn
字节码的内容要比 Java 源代码多很多。首先,可以看到 lookupswitch 指令已经被 switch() 语句使用到字节码中。但是,有两个 lookupswitch 指令而不是一个。反编译后可以看到 int 传入 switch() 语句,但是只有一个 lookupswitch 指令用到了。也就是说 switch() 语句被拆分成了两个语句来处理字符串。查看 #5、#39 和 #53 号字节指令看 switch() 语句是如何处理字符串的。
在 #5 和 #8 号字节中,首先,hashCode() 方法被执行,然后 switch(int) 是通过使用 hashCode() 方法的运行结果来执行的。在 lookupswitch 指令的括号中,分支根据 hashCode 的值来定位到不同地方。字符串 “abc” 的 hashCode 结果是 96354 ,被定位到 #36 号字节。字符串 “123” 的 hashCode 结果是 48690 ,被定位到 #50 号字节。
在 #36、#37、#39 和 #42 号字节中,可以发现接收的 str 变量值作为参数与 String “abc” 和 equals() 方法比较。如果结果相同,“0” 被插入到 #3 号本地变量列表的索引位置,字符串被移动到 #61 号字节。
通过这种方式,在 #50、#51、#53 和 #56 字节中,可以发现接收的 str 变量值作为参数与 String “123” 和 equals() 方法比较。如果结果相同,“1” 被插入到 #3 号本地变量列表的索引位置,字符串被移动到 #61 号字节。
在 #61 和 #62 号字节中,以 #3 号本地变量列表的索引位置的值,如:“0”、“1” 或其他值,进行 lookupswitch 和分支处理。
换句话说,在 Java 代码中,接收的 str 变量值作为 switch() 参数使用 hashCode() 方法和 equals() 方法进行比较。switch() 方法根据 int 值的结果执行。
在这个结果中,被编译的字节码与之前的 JVM 规范文档并没有任何不同。Java SE 7 的新特性,字符串分支语句,是由 Java 编译器来处理的,而不是 JVM 本身。以类似的方式,Java SE 7 的其他新特性也是通过 Java 编译器进行处理的。
总结
这里评审 Java 语言是如何设计让 Java 可以更容易的使用没有太大必要。有很多 Java 程序员并没有对 JVM 有很深入的了解,却也开发出了很多优秀的应用和库。不过,如果能够深入理解 JVM ,我们就能处理这些例子中出现的问题。除了这里提到的内容,JVM 有很多特性和技术。JVM 规范文档为 JVM 厂商提供了灵活的空间,让他们应用各种不同的技术从而提供更好的性能。特别是垃圾回收技术,它被大多数语言使用,提供与 VM 类似的使用方式以及最新最前沿的性能优化技术。但是,这些内容在很多卓越的研究中都会被讨论到,在此不作深入解释。
参考
参考来源:JVM Specification SE 7 - Run-Time Data Areas
2011.01 Java Bytecode Fundamentals
2012.02 Understanding JVM Internals
2013.04 JVM Run-Time Data Areas
Chapter 5 of Inside the Java Virtual Machine
2012.10 Understanding JVM Internals, from Basic Structure to Java SE 7 Features
2016.05 深入理解java虚拟机
结束
相关文章推荐
- JVM 内部原理(二)— 基本概念之字节码
- JVM 内部原理(三)— 基本概念之类文件格式
- Java内存模型及GC原理java内存模型 Java内存模型及GC原理 Java内存模型及GC原理 sun官方网站:sun java 虚拟机模型 Java内存模型及GC原理 JVM内存模型中分两
- JVM 内部原理(四)— 基本概念之 JVM 结构
- 有关JAVA虚拟机规范的官方文档 收藏
- Java 虚拟机(JVM)之基本概念详解
- JVM 内存基础概念之初识虚拟机与 Java 虚拟机运行时数据区
- Java编程的逻辑 (35) - 泛型 (上) - 基本概念和原理
- Java知多少(2)虚拟机(JVM)以及跨平台原理
- 该文简要描述了DOM概念和内部逻辑结构,给出了DOM文档操作和XML文件互相转换java实现过程。
- 【iOS官方文档翻译】iOS蓝牙的基本概念
- Java虚拟机规范----JVM体系结构
- [原][译][physX]phsyX3.3.4官方文档物理引擎基本概念和例子介绍
- java内功 ---- jvm虚拟机原理总结,侧重于GC
- jvm虚拟机基本概念
- jvm虚拟机内部原理之概述和内存分配
- java日期格式规范(JDK1.8官方文档)
- JVM原理二----JAVA虚拟机体系结构
- 1. ES 5.2 官方文档-基本概念
- 【SUN/Oracle官方文档翻译+纠错】JAVA语言编码规范