注:本文主要参考自《深入理解java虚拟机(第二版)》

1、javap的使用与类文件结构

使用过程:

java源代码:

 package compile;
/**
* class字节码
*/
public class TestClass {
private int m; public int inc() {
return m + 1;
}
}

在硬盘上找到java源文件所在目录(eg.E:\Java\workspaceOfMyBatis3\baseUtil\src\compile)

打开命令窗口,执行"javac -g TestClass.java"生成TestClass.class字节码文件,然后使用"javap -c TestClass > TCC.txt"将字节码文件的处理结果输出到TCC.txt中。

打开TCC.txt,如下:

Compiled from "TestClass.java"
public class compile.TestClass extends java.lang.Object{
public compile.TestClass();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return public int inc();
Code:
0: aload_0
1: getfield #2; //Field m:I
4: iconst_1
5: iadd
6: ireturn
}

说明:

  • javac -g TestClass.java

    • -g:生成所有的调试信息,包括局部变量名和行号信息。
  • javap -c TestClass > TCC.txt,对于javap常用的参数:
    • -c:输出字节码Code
    • -l(小写L):输出Code、LineNumberTable与LocalVariableTable
    • -s:输出方法签名(方法的接收参数列表和返回值)
    • -verbose:包含-c、-l以及输出class文件的编译版本,常量池,Stack, Locals, Args_size
  • 对于javap而言,常用的就是-c或-verbose  

这里列出使用"javap -verbose TestClass > TCV.txt的结果:

Compiled from "TestClass.java"
public class compile.TestClass extends java.lang.Object
SourceFile: "TestClass.java" /* 源文件名称 */
minor version: 0 /* 次版本号 */
major version: 50 /* 主版本号,50-->jdk6 */
Constant pool: /* 常量池:存放所有的方法名、field名、方法签名(方法参数+返回值)、类型名、class文件中的常量值 */
const #1 = Method #4.#18; // java/lang/Object."<init>":()V
const #2 = Field #3.#19; // compile/TestClass.m:I
const #3 = class #20; // compile/TestClass
const #4 = class #21; // java/lang/Object
const #5 = Asciz m; /*field名*/
const #6 = Asciz I; /*类型名*/
const #7 = Asciz <init>; /*方法名(构造器)*/
const #8 = Asciz ()V; /*方法签名(方法参数+返回值)*/
const #9 = Asciz Code;
const #10 = Asciz LineNumberTable; /*class文件中的常量值:Java源码的行号与字节码指令对应关系*/
const #11 = Asciz LocalVariableTable; /*class文件中的常量值:局部变量表*/
const #12 = Asciz this;
const #13 = Asciz Lcompile/TestClass;; /*当前类的类型"Lxxx;"表示xxx引用类型*/
const #14 = Asciz inc; /*方法名*/
const #15 = Asciz ()I; /*方法签名(方法参数+返回值)*/
const #16 = Asciz SourceFile; /*class文件中的常量值:源文件名称*/
const #17 = Asciz TestClass.java; /*class文件中的常量值:源文件名称*/
const #18 = NameAndType #7:#8;// "<init>":()V
const #19 = NameAndType #5:#6;// m:I
const #20 = Asciz compile/TestClass; /*类型名*/
const #21 = Asciz java/lang/Object; /*类型名*/ {
public compile.TestClass();
Code: /* 方法字节码 */
/* Stack:操作数栈的深度(这个值就是类加载阶段为操作数栈分配的深度)
* Locals:局部变量的分配空间(单位是slot,不是个数),对于double和long这两个64bit的,需要两个slot,对于其他<=32bit的,只需要一个slot
* Args_size:方法参数的个数,包括方法参数、this(this只针对实例方法,static方法不会自动添加this)
*/
Stack=1, Locals=1, Args_size=1
0: aload_0 /*将第0个Slot中的引用类型的本地变量推到操作数栈顶,这里就是LocalVariableTable的this*/
1: invokespecial #1; //Method java/lang/Object."<init>":()V /* invokespecial #1:调用#1常量代表的方法,这里就是super(),当前栈顶的元素作为该方法#1的接收者 */
4: return /*返回该方法,该方法的返回值为Void,执行了return指令,方法结束*/ LineNumberTable: /* Java源码的行号与字节码指令对应关系 */
line 5: 0 LocalVariableTable: /* 局部变量表 */
Start Length Slot Name Signature
0 5 0 this Lcompile/TestClass; public int inc();
Code:
Stack=2, Locals=1, Args_size=1
0: aload_0 /*将第0个Slot中的引用类型的本地变量推到操作数栈顶,这里就是LocalVariableTable的this*/
1: getfield #2; //Field m:I /*getfield #2:获取常量表中定义的#2实例(即实例m),然后将m推到操作数栈顶*/
4: iconst_1 /*向栈顶压入一个int常量1*/
5: iadd /*将栈顶的两个元素相加(这里是1和m),然后将结果压入栈顶*/
6: ireturn /*从当前方法返回栈顶的int型数值结果*/
LineNumberTable:
line 9: 0 LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcompile/TestClass;
}

说明:

  • 上述文件中/*xxx*/这样的注释是我添加的,//这样的注释是javap自己生成的
  • 需要知道的是,上述的文件并非是生成的*.class文件,*.class文件的内容是一串接近于机器码的十六进制字符,开头是一个魔数"0xCAFEBABE",该魔数是确定一个文件是否是class文件的标准。之后就是class编译版本(minor version,major version),然后下边的顺序与TCV.txt的顺序一样了。
  • 在TCV.txt文件中,多了一个无参构造器方法,该无参构造器调用的是TestClass的父类Object的无参构造器(即执行了super()方法),这个无参构造器是在javac变异的第三步"语义分析"的时候添加的,具体的查看第二章 Javac编译原理

注意:

  • 常量池的存放内容

    • 存放所有的方法名
    • field名
    • 方法签名(方法参数+返回值)
    • 类型名
    • class文件中的常量值
  • 常量池的前四部分可以称作是符号引用(即只有一些名称,但没有实际的地址,在运行期进行类的加载过后,会为这些东西分配实际的内存,到时候符号引用就会转化为直接引用,就能被JVM用了)
  • 常量池的组成:符号引用、常量(这个常量包含我们代码中定义的常量,eg、字符串常量,也包括class文件中的常量,eg.SourceFile)。
  • 主版本号的对应(eg.50对应jdk6,51对应jdk7),查看《深入理解java虚拟机(第二版)》P167
  • Stack:操作数栈的深度(这个值就是类加载阶段为操作数栈分配的深度)
  • Locals:局部变量的分配空间(单位是slot,不是个数),对于double和long这两个64bit的,需要两个slot,对于其他<=32bit的,只需要一个slot
  • Args_size:方法参数的个数,包括方法参数、this(this只针对实例方法,static方法不会自动添加this)
  • inc()方法:我详细注释了该方法的执行过程,这也就是JVM执行一个方法的基本流程(基于栈)

提醒:

  • Code部分是我们主要关注的部分,这一部分中关键的部分就是每一条字节码指令的意义是什么。具体的可以查看《深入分析Java Web技术内幕(修订版)》P124-P135

总结:

  • 掌握类文件结构,有利于我们理解类加载机制,而了解了类加载机制,最直接的好处,就是我们可以自己编写类加载工具,例如,smarty框架就是自己编写了一个类加载器
  • 读懂执行javap之后的字节码指令有利于我们理解java代码的执行流程,对我们定位问题也有一定的好处(虽然我在开发中还没有用这种方式定位过问题)

第三章 类文件结构与javap的使用的更多相关文章

  1. 深入理解Java虚拟机学习笔记(三)-----类文件结构/虚拟机类加载机制

    第6章 类文件结构 1. 无关性 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(即扩展名为 .class 的文件) 是构成平台无关性的基石. 字节码(即扩展名为 .class 的文 ...

  2. JVM学习笔记-第六章-类文件结构

    JVM学习笔记-第六章-类文件结构 6.3 Class类文件的结构 本章中,笔者只是通俗地将任意一个有效的类或接口锁应当满足的格式称为"Class文件格式",实际上它完全不需要以磁 ...

  3. 《深入理解java虚拟机》第六章 类文件结构

    第六章 类文件结构   6.2 无关性的基石 各种不同平台的虚拟机与所有的平台都统一使用的程序存储格式--字节码(ByteCode)是构成平台无关性的基石.java虚拟机不和包括java在内的任何语言 ...

  4. 《UML精粹》第三章 -类图的基本概念

    第三章 类图:基本概念 类图可用来描写叙述系统中各种对象的类型.也可描绘出对象间各种各样的静态关系.此外.类图中也能够秀出类的性质(property)与操作(operation),以及可应用到对象间连 ...

  5. Java程序设计(2021春)——第三章类的重用笔记与思考

    Java程序设计(2021春)--第三章类的重用笔记与思考 本章概览: 3.1 类的继承(概念与语法) 3.2 Object类(在Java继承最顶层的类) 3.3 终结类和终结方法(只能拿来用,不可以 ...

  6. 《深入理解Java虚拟机》-----第6章 类文件结构——Java高级开发必须懂的

    代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步. 6.1 概述 记得在第一节计算机程序课上我的老师就讲过:“计算机只认识0和1,所以我们写的程序需要经编译器翻 ...

  7. 类文件结构与javap的使用

    此文已由作者赵计刚薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.javap的使用与类文件结构 使用过程: java源代码:  1 package compile;   ...

  8. JVM学习笔记(三):类文件结构

    代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步. 实现语言无关性的基础是虚拟机和字节码存储格式.Java虚拟机不和包括Java在内的任何语言绑定,只与&quo ...

  9. JAVA基础第三章-类与对象、抽象类、接口

    业内经常说的一句话是不要重复造轮子,但是有时候,只有自己造一个轮子了,才会深刻明白什么样的轮子适合山路,什么样的轮子适合平地! 我将会持续更新java基础知识,欢迎关注. 往期章节: JAVA基础第一 ...

随机推荐

  1. 【运维实战】一次linux日志分割之路——将日志按照每小时进行分割,并按照“日期-小时”格式保存

    是这样的,现在需要对nginx的access.log进行按照每小时进行分割,并且最好还要能够以 “日期+时间”的形式,命名保存. 两点,一个是按照每小时进行分割,一个是将日志以“日期+时间”的形式进行 ...

  2. JAVA特性-跨平台/面向对象

    JAVA特点概述 一,跨平台 这无疑是java最大的特点了,我相信大多数人第一次听说java语言大都从跨平台开开始的.实际上java跨平台特性主要体现在两个方面:编码和运行机制. 1,编码 java语 ...

  3. 三、django rest_framework源码之权限流程剖析

    1 绪言 上一篇中分析了认证部分的源码,认证后的下一个环节就是权限判定了.事实上,权限判定肯定要与认证联合使用才行,因为认证部分不会对请求进行禁止或者是允许,而只是根据请求中用户信息进行用户身份判断, ...

  4. JAVAEE——SSH项目实战03:新增客户、数据字典、文件上传和修改客户

    作者: kent鹏 转载请注明出处: http://www.cnblogs.com/xieyupeng/p/7145599.html 一.新增客户 1.数据字典  用于枚举项目中有限个数的字典项 (1 ...

  5. 世界杯:用Python分析热门夺冠球队-(附源代码)

    2018年,火热的世界杯即将拉开序幕.在比赛开始之前,我们不妨用 Python 来对参赛队伍的实力情况进行分析,并大胆的预测下本届世界杯的夺冠热门球队. 通过数据分析,可以发现很多有趣的结果,比如: ...

  6. UOJ275 组合数问题

    给定n,m和k,求有多少对(i , j)满足0 ≤ i ≤ n, 0 ≤ j ≤ min(i ,m)且C(︀i,j)︀是k的倍数.n,m ≤ 1018, k ≤ 100,且k是质数. 把i和j都看成k ...

  7. apt-get出现无法定位安装包问题解决

    这个问题出现在sources.list上 编辑/etc/apt/sources.list下的文件 找到检查你的存储库是否正确 你可以在以下页面找到不同版本 Kali Linux 的存储库:http:/ ...

  8. POJ 1321 棋盘问题 DFS 期末前水一水就好……

    A - 棋盘问题 Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Sta ...

  9. ROS知识(16)----如何编译时自动链接同一个工作空间的其他包的头文件(包含message,srv,action自动生成的头文件)

    catkin_make编译时,往往需要自动链接同一个工作空间的其他包的头文件.否则会出现类似如下的错误: /home/xx/xx_ws/srcA_package/src/db.hpp:13:26: f ...

  10. 华为S5300系列交换机V200R001SPH027升级补丁

    S5300SI-V200R001SPH027.pat 附件: 链接:https://pan.baidu.com/s/1ulE0j5Rp4xMkAaOjNGKLMA  密码:d5ze