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. 微信小程序flex布局

    一.flex布局基础 二.相对定位和绝对定位   flex的容器和元素   主轴(左-右),交叉轴(上-下)     flex容器属性详解 flex-direction 决定元素的排列方向(默认row ...

  2. Windows Phone8.1应用 提交

    昨天晚上,试了试把一个WP8.1应用提交到微软商店上去,期间遇到了不少问题,搞到凌晨还没弄好.今天早上,终于把WP8.1应用成功提交上去了. 下面说说如何把WP8.1应用提交商店去.提交WP8.1以及 ...

  3. (Android MVVM)使用Data Binding Library(1)

    Data Binding Library 用官方提供的Data Binding Library,可以最大限度的减少findViewById(),setOnClickListener()之类的胶水代码, ...

  4. redis 在windows 集群

    前言:为什么自己要花时间写一篇redis集群文章,网上众多的文章大都是思路正确,但是细节不足,这里写一篇文章记录自己部署时候遇到的问题,当下次再部署的时候避免跳入重复的坑. 上篇文章(http://w ...

  5. CSS中常用属性之字体属性

    1,以下是CSS中常用字体属性: font-family             字体样式 font-size                字体大小 font-size-adjust  为元素规定 ...

  6. CAD参数绘制多行文字(com接口)

    在CAD设计时,需要绘制多行文字,用户可以设置设置绘制文字的高度等属性. 主要用到函数说明: _DMxDrawX::DrawMText 绘制一个多行文字.详细说明如下: 参数 说明 DOUBLE dP ...

  7. vue组件之this指向问题

    [问题描述] 返回顶部组件里,用到数据操作.通过方法里改动this数据,但发现直接使用失效 mounted() { window.onscroll=function(){ ) { this.isAct ...

  8. spring的设计思想

    在学习Spring框架的时候, 第一件事情就是分析Spring的设计思想 在学习Spring的时候, 需要先了解耦合和解耦的概念 耦合: 简单来说, 在软件工程当中, 耦合是指对象之间的相互依赖 耦合 ...

  9. react.js工程结构

    1.index.html :UI界面入口.挂在点: 2.manifest.json:应用说明 3.package.json:工程说明.依赖说明等 4.source : 代码源文件

  10. 2.10.4 aside元素

    aside元素 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> < ...