JVM(三):深入分析Java字节码-上

字节码文章分为上下两篇,上篇也就是本文主要讲述class文件存在的意义,以及其带来的益处。并分析其内在构成之一 ———字节码,而下篇则从指令集方面着手,讲解指令集都有哪些,以及其各自代表的含义。最后总结一下Class文件存在的必然性。

意义

前面说过 Java 虚拟机拥有平台无关性,但其实现在语言无关性在 JVM 和更加的体现了出来。表现就是目前越来越多的语言可以在 JVM 上运行,而这背后的逻辑,就是这些语言都会被编译为 Class 文件,然后在JVM上 运行。

In the future,we will consider bounded extensions to the java virtual machine to provide better support for other languages.

上面这段是 JVM 的相关人员提出的愿景,因此我们也对 Java 语言的发展更加的看好。

那么在下面的文章中,我们就来探讨 Class 文件的组成部分,了解其内部是如何组织的。

Class类文件

首先我们编写一个原始的Java源代码:

public class TestForEach extends Thread{
private static int ccc = 1;
public static void main(String[] args) { int a = 1;
int b = 2;
int c = a + b;
}

这里我们使用JDk提供的工具对代码进行编译,得到下面这个二进制流。

    cafe babe 0000 0034 001d 0a00 0600 0f09
0010 0011 0800 120a 0013 0014 0700 1507
0016 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 046d 6169
6e01 0016 285b 4c6a 6176 612f 6c61 6e67
2f53 7472 696e 673b 2956 0100 0a53 6f75
.........

对我们来说,所需要分析的就是这个文件。该二进制流的前4个字节cafe babe,其被称为魔数。它代表了这是一个.class类型的文件,紧接着的第五第六个字节为次版本号,第七第八个字节为主版本号,而我们编译的这个版本是在 JDK1.8 下。再紧接着就是常量池,访问标示等信息,因为这些信息计算十分的繁琐麻烦,在这里就不展开来计算了,而官方也细心地提供了javap 工具进行反编译来查看其组织形式。

字节码文件

首先我们采用javap工具进行反编译javap -verbose TestForEach,得到如下文件

      Last modified 2019-5-22; size 461 bytes
MD5 checksum 2602dfd883d5d5e417e26ce2d42b916d
Compiled from "TestForEach.java"
public class TestForEach extends java.lang.Thread
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#22 // java/lang/Thread."<init>":()V
#2 = Fieldref #5.#23 // TestForEach.d:I
#3 = Fieldref #5.#24 // TestForEach.dfinal:I
#4 = Fieldref #5.#25 // TestForEach.ccc:I
#5 = Class #26 // TestForEach
#6 = Class #27 // java/lang/Thread
#7 = Utf8 ccc
#8 = Utf8 I
#9 = Utf8 d
#10 = Utf8 dfinal
#11 = Utf8 ConstantValue
#12 = Integer 2222
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 <clinit>
#20 = Utf8 SourceFile
#21 = Utf8 TestForEach.java
#22 = NameAndType #13:#14 // "<init>":()V
#23 = NameAndType #9:#8 // d:I
#24 = NameAndType #10:#8 // dfinal:I
#25 = NameAndType #7:#8 // ccc:I
#26 = Utf8 TestForEach
#27 = Utf8 java/lang/Thread
{
public TestForEach();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Thread."<init>":()V
4: aload_0
5: iconst_1
6: putfield #2 // Field d:I
9: aload_0
10: sipush 2222
13: putfield #3 // Field dfinal:I
16: return
LineNumberTable:
line 11: 0
line 17: 4
line 18: 9 public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: iconst_2
1: istore_2
2: iconst_1
3: iload_2
4: iadd
5: istore_3
6: return
LineNumberTable:
line 22: 0
line 23: 2
line 111: 6 static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: iconst_1
1: putstatic #4 // Field ccc:I
4: return
LineNumberTable:
line 16: 0
}
SourceFile: "TestForEach.java"

文件头

我们从头开始看起,首先是

      minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER

这里分别为Class文件的次版本和主版本号以及访问标示,版本号前面已经说过了,这里就不多赘述了,至于访问标志,其代表了一些类或接口的访问信息,如是否是public类型的,有没有被声明成final等等。

常量池

接着到了 ConstantPool常量池,其可以简单理解为Class文件中的资源仓库,许多部分都与其关联。

其中存放了字面量和符号引用两种类常量。字面量可以理解为文本字符串和声明为final的常量值等。符号引号则包含以下内容:

  • 类和接口的全限定名;
  • 字段的名称和描述符;
  • 方法的名称和描述符。

上面两种类型在常量池中都是以表的形式来存储,其具体含义如下图所示:

针对常量池内每个表的含义和我们得到的class文件,在这作者分析几个看一下:

  • Methodref:方法的符号引用,具体内容跳到6和22行,但从后面的注释可以看到是父类Thread的构造方法
  • Fieldref:字段的符号引用,具体内容在5,23行,可以看到是在TestForEach类中定义了int类型的属性d
  • Class:类或接口的符号引用,从中可以看出该文件是TestForEach类,继承自Thread
  • Utf8:表明其是一个字符串,在文件中字段描述ccc,d,dfinal,全限定名java/lang/Thread等,方法描述main,()V等都属于这一类
  • NameAndType:字段或方法的符号引用,与上面不同的是,其没有声明是哪一个类的
  • .....

更多的这里就不详细展开了,但常量池中还有一些上面没有提到的内容,在这里我们细说一下.

属性表集合

属性表集合用于描述某些场景中专有的信息.针对上文出现的属性表,我们这里详细说下.

  • ConstantValue:final关键字定义的常量值;
  • Code:Java代码编译而成字节码指令,具体指令含义在下篇中细说;
  • LineNumberTable:Java源码行号和字节码指令的对应关系;
  • SourceFile:源文件的名称
  • .........

总结

本文讲了 Class 文件在 Java 达成平台无关性和语言无关性起到的重要作用,并叙述了Class文件的重要组成部分----文件标识,常量池,属性集合等。此外还对文件标识和常量池的内容进行了具体的展开描述。

下篇文章则从 JVM 支持的指令集内容开始介绍,并以本文的 Class 文件的指令集集合为例,具体描述一下指令集的内容。

文章在公众号"iceWang"第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯。

本系列文章主要借鉴自《深入分析JavaWeb技术内幕》和《深入理解Java虚拟机-JVM高级特性与最佳实践》。

JVM(三):深入分析Java字节码-上的更多相关文章

  1. JVM(四):深入分析Java字节码-下

    JVM(四):深入分析Java字节码-下 在上文中,我们讲解了 Class 文件中的文件标识,常量池等内容.在本文中,我们就详细说一下剩下的指令集内容,阐述其分别代表了什么含义,以及 JVM 团队这样 ...

  2. 【JVM源码解析】模板解释器解释执行Java字节码指令(上)

    本文由HeapDump性能社区首席讲师鸠摩(马智)授权整理发布 第17章-x86-64寄存器 不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Archit ...

  3. JVM 内部原理(七)— Java 字节码基础之二

    JVM 内部原理(七)- Java 字节码基础之二 介绍 版本:Java SE 7 为什么需要了解 Java 字节码? 无论你是一名 Java 开发者.架构师.CxO 还是智能手机的普通用户,Java ...

  4. JVM 内部原理(六)— Java 字节码基础之一

    JVM 内部原理(六)- Java 字节码基础之一 介绍 版本:Java SE 7 为什么需要了解 Java 字节码? 无论你是一名 Java 开发者.架构师.CxO 还是智能手机的普通用户,Java ...

  5. 深入了解java虚拟机(JVM) 第十章 字节码指令

    一.字节码指令的含义 Java字节码指令由一个字节长度的,代表某种特定操作含义的数字(操作码)以及其后的零至多个代表此操作所需参数(操作数).此外字节码指令是面向操作数栈的,这里操作数栈在功能上对应实 ...

  6. JVM Java字节码的角度分析switch的实现

    目录 Java字节码的角度分析switch的实现 引子 前置知识 一个妥协而又枯燥的方案 switch的实现 回顾历史 字节码分析 其他实现方式? Java字节码的角度分析switch的实现 作者 k ...

  7. 通过Java字节码发现有趣的内幕之String篇(上)(转)

    原文出处: jaffa 很多时候我们在编写Java代码时,判断和猜测代码问题时主要是通过运行结果来得到答案,本博文主要是想通过Java字节码的方式来进一步求证我们已知的东西.这里没有对Java字节码知 ...

  8. JVM 字节码指令手册 - 查看 Java 字节码

    JVM 字节码指令手册 - 查看 Java 字节码 jdk 进行的编译生成的 .class 是 16 进制数据文件,不利于学习分析.通过下命令 javap -c Demo.class > Dem ...

  9. 掌握Java字节码(转)

    Java是一门设计为运行于虚拟机之上的编程语言,因此它需要一次编译,处处运行(当然也是一次编写,处处测试).因此,安装到你系统上的JVM是原生的程序,而运行在它之上的代码是平台无关的.Java字节码就 ...

随机推荐

  1. 一种结合hudson的算法自动化测试构想

    作者:朱金灿 来源:http://blog.csdn.net/clever101 有时我在思考:未来软件测试的趋势是什么?其实答案和其它行业一样简单:低技术含量的测试工作都将由机器承担,人只能干机器干 ...

  2. 使用Jenkins进行android项目的自动构建(5)

    之前在项目中引入的单元测试使用的是JUnit,可以在构建前进行测试,这里在介绍一下使用Instrumentation 进行单元测试.使用Instrumentation进行测试,比之前多一些步骤,需要把 ...

  3. 使用Jenkins进行android项目的自动构建(3)

    建立Jenkins项目 1. “新增作业”->填写作业名称->选择“建置 Maven 2 或 3 專案”->OK.新增成功后会进入“組態設定”,暂时先保留默认值,稍后再进行设定. 2 ...

  4. Java之抽象和封装

    ① 如何从现实世界中抽象出类?    根据软件开发需求: 发现类-->发现类的属性-->发现类的方法    ②   构造方法的作用和特点是什么?    作用:在创建对象时执行一些初始化操作 ...

  5. du查看文件大小

    du+文件名就可以查看文件大小 du+ -h + 文件名也是查看文件大小,只是-h会将文件大小转换成M,G等格式

  6. 在mac下做web开发,shell常用的快捷键

    Ctrl + A 光标移动到行首 Ctrl + E 光标移动到行末 Ctrl + K 清屏(也可是用clear命令) Command +shift+{} 终端的tab左右切换

  7. 第4节 hive调优:1、2、fetch抓取和表的优化

    hive的调优:第一个调优:fetch抓取,能够避免使用mr的,就尽量不要用mr,因为mr太慢了 set hive.fetch.task.conversion=more 表示我们的全局查找,字段查找, ...

  8. js中事件冒泡和事件捕获

    什么时候存在这种问题? 当一个行为触发了多个对象的事件时.   <body>   <div class="fa">     <div class=&q ...

  9. 笔试算法题(28):删除乱序链表中的重复项 & 找出已经排好序的两个数组中的相同项

    出题:给定一个乱序链表,节点值为ASCII字符,但是其中有重复项,要求去除重复项并保证不改变剩余项的原有顺序: 分析:创建一个256(2^8)大小的bool数组,初始化为false,顺序读取链表,将字 ...

  10. 小甲鱼python疑难点

    1.python生成器 2.while 1: num = input('请输入一个整数(输入Q结束程序):') if num != 'Q': num = int(num) print('十进制 -&g ...