目录

前言

最近在研究Java的反射和动态代理,发现使用这两个Java神器需要了解.class文件的字节码。于是翻阅了相关资料,在这篇博客中进行一番整理,也作为自己学习的记录。

如何阅读class文件

Java的可移植性是基于.java文件编译后形成的唯一的字节码文件.class文件可以在不同操作系统上的jvm运行的机制。.class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在.class文件中,中间没有任何分隔符。

当程序员编译了.java文件后,在指定的路径下会生成一个.class文件,使用editplus可以直接以Hex viewer的格式打开.class文件

ClassTest.java

package com.classloader;
public class ClassTest {
    public static void main(String[] args) {
        System.out.println("Hello,World!");
    }
}

ClassTest.class

要想能读懂class文件这种生肉干货,首先要理解.class文件中的一些基本概念

基本概念

无符号数&表

无符号数是一种基本的数据类型,通常用u1,u2,u4,u8代表1、2、4、8个字节的无符号数。

无符号数是用来描述数字、索引引用、数量值或者UTF-8编码的字符串值,可以称作是.class文件的基本组成单位

表是由多个无符号数或其他表构成的复合数据类型,整个.class文件的本质就是一个表。(表大都习惯性的以_info结尾)

无论是无符号数,还是表,当需要描述同一类型但数量不定的多数据的时候,经常会使用一个位置的容量计数器加若干个连续的数据项的形式。

常量池

魔数(magic number) & 版本号

每个.class文件的头四个字节被称为“魔数”,其作用是确定该.class文件是否为一个能被HOTSPOT虚拟机接收的.class文件

魔数后面的四个字节是版本号,第五和第六个字节是“次版本号”,第七和第八个字节是“主版本号”。

Java的版本号是从45开始的,自jdk1.1之后的每个jdk大版本发布的主版本号都向上+1,并且高版本的jdk能向下兼容以前版本的.class文件。(注意:hex viewer下,.class文件中的数字都是16进制数)

常量池

在主版本号后面的是常量池入口,常量池是.class文件结构中与其他项目关联最多的数据类型,也是占用.class文件空间最大的数据项目之一。

由于常量池中的常量的数量是不固定的,所以常量池的入口需要放置一项u2类型的数据,代表常量池容量计数。这个容量计数是从1开始的(有别于传统的程序员计数法则)。

上图中的.class文件的常量池计数是34,由于从1开始,所以常量的个数是33(十六进制的22是十进制的34)。也就是说,从计数位之后的33个表,都是表示常量的。

常量池中主要存放两大类常量:字面量(literal)和字符引用(Symbolic References)。

字面量比较接近于Java语言层的常量概念,例如文本字符串、被声明为final的常量值等等。

字符引用包括三类变量:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。

刚刚提到过,常量池中的每一个常量都是一个表,一共11种结构各不相同的表,这些表都有一个共同的特点,开始的第一位是一个u1类型的标志位,代表当前这个常量属于哪种类型。(具体查看【查阅表格】)

总而言之,查看常量的方法就是:

1.第一个字节为tag 查看常量池类型表找到对应的类型

2.找到对应结构的表,找到tag之后属于常量的其他无符号数

访问标志

常量池结束后,紧接着的两个字节表示访问标志(access_flags)。这个标志用来识别一些类或接口层次的访问信息,包括:这个classs是实体类还是接口;是否定义为public;是否是抽象类;是否为final类等等。

具体访问标志的映射详见【查阅表格】

类引索&父类引索&接口引索集合

类引索(this_class)和父类引索(super_class)都是一个u2类型的数据,接口引索集合是一组u2类型的数据集合。

在访问标志之后,紧接着是类引索、父类引索,共占据4个字节。他们各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info类型的常量中的索引值可以找到定义在CONSTANT_Utf8_info类型常量中的全限定名字符串。

字段表集合

在接口索引集合后的两个字节是fields_count类型,描述的是字段表集合内有多少个字段表。

字段表对应的是程序员在.java文件中的字段,字段表的各类型分别对应修饰词、引用名称等等

字段表的阅读方法和常量的阅读方法一致。

字段表结构以及字段表中各结构类型详见【查阅表格】

方法表集合

在字段表集合结束后,接下来的两个字节是method_count类型,描述的是方法表集合中有多少个方法表。

方法表对应的是程序员在.java文件中编写的方法,方法表的各类型分别对应修饰词、引用名称等等。

方法表结构以及方法表中各结构类型详见【查阅表格】

属性表集合

方法表集合之后四个字节,描述的是属性表集合。.class文件有很多属性,每个属性,它的名称需要从常量池中引用一个CONSTANT_Utf8_info类型的常量表示。

Code属性

Java程序方法体内的代码经过javac编译处理之后,最终编程字节码指令存储在Code属性内。这之后就涉及到了字节码执行引擎的问题,之后会在其他的博客中进行讲解,敬请期待。

在属性表集合之后就是Code属性,具体对应的类型详见【查阅表格】

使用javap解析class文件

对于.class文件的解析工作,jdk为我们提供了类解析工具javap。javap生成的.class文件解析比较直观,容易理解,算是半生肉。结合上文讲述的各个概念,应该不难理解。

具体使用方法是在cmd中输入:

javap -verbose 类名

输出结果大致是这样:(以ClassTest.class为例)

查阅表格

常量池类型表

所有结构类型

访问标志

字段表结构

字段表访问标志



各标志的含义和其后半段的内容一致,表示字段的修饰符

描述符标志字符含义

对于数组类型,每一位都使用一个前置的“[”来描述。比如一个java.lang.String[][] 被记录为 [[Ljava/lang/String

一个int[]被记录为[I

方法表结构

方法访问标志

Code 属性

2020年01月14日 17时03分40秒

打赏一下:

干货!直击JVM底层 —— Java Class字节码文件解析的更多相关文章

  1. JDK动态代理[4]----ProxyGenerator生成代理类的字节码文件解析

    通过前面几篇的分析,我们知道代理类是通过Proxy类的ProxyClassFactory工厂生成的,这个工厂类会去调用ProxyGenerator类的generateProxyClass()方法来生成 ...

  2. [置顶] Java字节码文件剖析

    Java为什么能够支持跨平台,其实关键就是在于其*.class字节码文件,因为*.class字节码文件有一个统一标准的规范,里面是JVM运行的时需要的相关指令,各家的JVM必须能够解释编译执行标准字节 ...

  3. 【JVM故事】一个Java字节码文件的诞生记

    万字长文,完全虚构. (一) 组里来了个实习生,李大胖面完之后,觉得水平一般,但还是留了下来,为什么呢?各自猜去吧. 李大胖也在心里开导自己,学生嘛,不能要求太高,只要肯上进,慢慢来.就称呼为小白吧. ...

  4. JAVA字节码文件之常量池

    一.常量池的内容 一个java类中定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是class文件的资源仓库,比如java类中定义的方法与变量信息.常量池中主要存储两类常量:字面量(文本字符 ...

  5. JAVA字节码文件之结构

    开发工具:IEDA.JDK1.8.WinHex 一.字节码文件结构 源代码 package com.jalja.java.bytecode; /** * @Auther: XL * @Date: 20 ...

  6. JAVA反射机制_获取字节码文件对象

    是在运行状态中,对于任意一个类 (class文件),都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性: 这种动态获取的信息以及动态调用对象的方法的功能称为java语 ...

  7. JAVA字节码文件之第三篇(访问标识)

    一.Access Flags 访问标志 访问标志信息包括该 Class 文件是类还是接口,是否被定义成 public 或者 abstract , 如果是类,是否被声明成 final. 访问标志表 二. ...

  8. JAVA字节码文件之第四篇(方法分析)

    一.Methods 方法字节码结构 Methods 字节码结构: Methods num:占两byte,Methods 的具体内存占n个byte 方法中每个属性都是Attribute_info,Att ...

  9. 命令行中运行Java字节码文件提示找不到或无法加载主类的问题

    测试类在命令行操作,编译通过,运行时,提示 错误: 找不到或无法加载主类 java类 package com.company.schoolExercise; public class test7_3_ ...

随机推荐

  1. laravel 是怎么做到运行 composer dump-autoload 不清空 classmap 映射关系的呢?

    我看 laravel 的 composer.json 文件 autoload 也没配置 vendor/autoload_classmap.php 里的映射关系,正常来说,如果没有配置,执行 compo ...

  2. Spring Boot Thymeleaf 使用详解

    在上篇文章Spring Boot (二):Web 综合开发中简单介绍了一下 Thymeleaf,这篇文章将更加全面详细的介绍 Thymeleaf 的使用.Thymeleaf 是新一代的模板引擎,在 S ...

  3. 2019-2-28-C#-16-进制字符串转-int-

    title author date CreateTime categories C# 16 进制字符串转 int lindexi 2019-02-28 11:51:36 +0800 2018-04-2 ...

  4. 常用mime.types

    以下是从nginx配置文件mime.types中提取出的最常用的文件格式, 整理了下, 方便查看 类型 文件格式 default_type application/octet-stream - tex ...

  5. js基础——正则表达式

    1.创建方式: var box = new RegExp('box');//第一个参数字符串 var box = new RegExp('box','ig');//第二个参数可选模式修饰符 等同于 v ...

  6. linux 手动睡眠

    在 Linux 内核的之前的版本, 正式的睡眠要求程序员手动处理所有上面的步骤. 它是一 个繁琐的过程, 包含相当多的易出错的样板式的代码. 程序员如果愿意还是可能用那种方 式手动睡眠; <li ...

  7. H3C查看历史命令--用户以上

    [H3Cwang]display history-command   sys   sysname H3C   sysname H3Cwang   display history-command   q ...

  8. hihocoeder1384

    hihocoeder1384 算法竞赛进阶指南上的题目 我们肯定是吧最大值和最小值匹配,次大值和次小值匹配以此类推 首先,类似于区间覆盖的思想,我们对于一个\(L\),找到最大的满足条件的\(R\) ...

  9. C# 匹配可空变量

    在 C# 7.0 的时候提供更好用的模式匹配方法,支持通过 is 直接转换对应的类,但是如果是尝试转换可空的对象,那么将会提示无法编译,或转换失败 在 C# 7.0 的 is 转换是十分好用的功能,例 ...

  10. CF1208

    CF1208 打的话貌似能够涨分的样子? A 水题 B 枚举左端点,看看右端点的最近位置 开一个类似于桶的东西维护一下上一次出现位置 左端点左边就删掉,否则就要将上一次出现的位置删掉 时间复杂度\(O ...