类文件结构与javap的使用
此文已由作者赵计刚薪授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
1、javap的使用与类文件结构
使用过程:
java源代码:
1 package compile;
2 /**
3 * class字节码
4 */
5 public class TestClass {
6 private int m;
7
8 public int inc() {
9 return m + 1;
10 }
11 }
在硬盘上找到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代码的执行流程,对我们定位问题也有一定的好处(虽然我在开发中还没有用这种方式定位过问题)
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 Jmeter压测Thrift服务接口
【推荐】 spark日志配置及问题排查方式。
【推荐】 知物由学 | 如何利用人工智能来对抗DDoS攻击?
类文件结构与javap的使用的更多相关文章
- 第三章 类文件结构与javap的使用
注:本文主要参考自<深入理解java虚拟机(第二版)> 1.javap的使用与类文件结构 使用过程: java源代码: package compile; /** * class字节码 */ ...
- 《深入理解Java虚拟机》类文件结构
上节学习回顾 在上一节当中,主要以自己的工作环境简单地介绍了一下自身的一些调优或者说是故障处理经验.所谓百变不离其宗,这个宗就是我们解决问题的思路了. 本节学习重点 在前面几章,我们宏观地了解了虚拟机 ...
- 深入理解java虚拟机【Java Class类文件结构】
Java语言从诞生之时就宣称一次编写,到处运行的跨平台特性,其实现原理是源码文件并没有直接编译成机器指令,而是编译成Java虚拟机可以识别和运行的字节码文件(Class类文件,*.class),字节码 ...
- (转)《深入理解java虚拟机》学习笔记5——Java Class类文件结构
Java语言从诞生之时就宣称一次编写,到处运行的跨平台特性,其实现原理是源码文件并没有直接编译成机器指令,而是编译成Java虚拟机可以识别和运行的字节码文件(Class类文件,*.class),字节码 ...
- 《深入理解java虚拟机》笔记——简析java类文件结构
一直不太搞得明确jvm究竟是如何进行类载入的,在看资料的过程中迷迷糊糊.在理解类载入之前,首先看看java的类文件结构究竟是如何的,都包含了哪些内容. 最直接的參考当然是官方文档:The Java® ...
- JVM笔记9-Class类文件结构
1.Class类文件结构 Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个 Class 文件中 ...
- 《深入理解Java虚拟机》-----第6章 类文件结构——Java高级开发必须懂的
代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步. 6.1 概述 记得在第一节计算机程序课上我的老师就讲过:“计算机只认识0和1,所以我们写的程序需要经编译器翻 ...
- JVM总结(三):类文件结构
这一节我们来总结一下类文件结构方面的知识.目录如下: 类文件结构 字节码的意义 Class类文件的结构 Class类文件的存储形式 Class文件的格式 Class类文件结构详解 举例详解 一.写程序 ...
- JVM学习笔记(三):类文件结构
代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步. 实现语言无关性的基础是虚拟机和字节码存储格式.Java虚拟机不和包括Java在内的任何语言绑定,只与&quo ...
随机推荐
- Hyberledger-Fabric 1.00 RPC学习(1)
参考:http://hyperledger-fabric.readthedocs.io/en/latest/write_first_app.html 本文的目的就是基于Hyperledger Fabr ...
- uva-11234-表达式
后缀表达式,使用队列计算,要求计算的结果一样,输出队列的输入串 表达式转二叉树,层次序遍历,先右孩子,然后字符串反转输出 #include <iostream> #include < ...
- 匹配ip等的正则式
- django2.0数据展示流程
之前刚刚实现了数据添加的流程,那么数据展示是怎么回事 1 先在 views.py 中定义函数 增加获取数据的方式 from django.shortcuts import render from bl ...
- 如何优化Java垃圾回收-zz
为什么需要优化GC 或者说的更确切一些,对于基于Java的服务,是否有必要优化GC?应该说,对于所有的基于Java的服务,并不总是需要进行GC优化,但前提是所运行的基于Java的系统,包含了如下参数或 ...
- TLS协议扫盲(握手,非对称加密,证书,电子签名等)
想学习TLS协议最好的方法应该是去看RFC,但如果对安全传输协议没有一些基本认识的人很难一上来就读懂RFC里面的种种细节和设计原则,所以这里为了能够进一步去弄懂TLS协议,把一些基本的知识放在这里,算 ...
- Supervisor安装与配置
Supervisor(http://supervisord.org/)是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统 ...
- linux下实时查看log
1.先切换到:cd usr/local/tomcat5/logs2.tail -f catalina.out3.这样运行时就可以实时查看运行日志了 Ctrl+c 是退出tail命令. 顺便讲一下lin ...
- ubuntu 安装google输入法
第五步:通常情况下,IBus图标(一个小键盘)会出现在桌面右上角的任务栏中.有时候这个图标会自行消失,可使用以下命令,找回消失的IBus图标: ibus-daemon -drx 不建议用googl ...
- php多进程pcntl学习(采集新浪微博)
上面2篇文都简明了多进程中一些需要注意的地方,这次用多进程配合curl_mulit_*来做新浪微博的采集. 先把知识点和值得注意的坑列出 /* 需求:开3个进程,并且模拟多线程来采集新浪微博用户信息, ...