此文已由作者赵计刚薪授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

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的使用的更多相关文章

  1. 第三章 类文件结构与javap的使用

    注:本文主要参考自<深入理解java虚拟机(第二版)> 1.javap的使用与类文件结构 使用过程: java源代码: package compile; /** * class字节码 */ ...

  2. 《深入理解Java虚拟机》类文件结构

    上节学习回顾 在上一节当中,主要以自己的工作环境简单地介绍了一下自身的一些调优或者说是故障处理经验.所谓百变不离其宗,这个宗就是我们解决问题的思路了. 本节学习重点 在前面几章,我们宏观地了解了虚拟机 ...

  3. 深入理解java虚拟机【Java Class类文件结构】

    Java语言从诞生之时就宣称一次编写,到处运行的跨平台特性,其实现原理是源码文件并没有直接编译成机器指令,而是编译成Java虚拟机可以识别和运行的字节码文件(Class类文件,*.class),字节码 ...

  4. (转)《深入理解java虚拟机》学习笔记5——Java Class类文件结构

    Java语言从诞生之时就宣称一次编写,到处运行的跨平台特性,其实现原理是源码文件并没有直接编译成机器指令,而是编译成Java虚拟机可以识别和运行的字节码文件(Class类文件,*.class),字节码 ...

  5. 《深入理解java虚拟机》笔记——简析java类文件结构

    一直不太搞得明确jvm究竟是如何进行类载入的,在看资料的过程中迷迷糊糊.在理解类载入之前,首先看看java的类文件结构究竟是如何的,都包含了哪些内容. 最直接的參考当然是官方文档:The Java® ...

  6. JVM笔记9-Class类文件结构

    1.Class类文件结构  Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个 Class 文件中 ...

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

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

  8. JVM总结(三):类文件结构

    这一节我们来总结一下类文件结构方面的知识.目录如下: 类文件结构 字节码的意义 Class类文件的结构 Class类文件的存储形式 Class文件的格式 Class类文件结构详解 举例详解 一.写程序 ...

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

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

随机推荐

  1. 【好文转帖】控制反转(IOC)和依赖注入(DI)的区别

    IOC   inversion of control  控制反转 DI   Dependency Injection  依赖注入 要理解这两个概念,首先要搞清楚以下几个问题: 参与者都有谁? 依赖:谁 ...

  2. div+css 命名规则

    编写网页的时候,一般都没有把命名规则认真对待.在结合语义以及搜索优化两方面给出了一套命名方案,对增强阅读性.规范性和提高开发效率是有帮助的,当然,这方面的规范,仁者见仁智者见智,网上有许多前辈们以经验 ...

  3. mac os 平台下载并编译android2.3.3源码

    现在在做有关android平台下的项目,最初对android环境各种不熟悉,搞了几个月终于有点眉目了,由于需要用到android本身提供的一些类似gps,tts等服务,单纯的看android提供的ja ...

  4. Node.js中的express框架获取http参数

    最近本人在学习开发NodeJs,使用到express框架,对于网上的学习资料甚少,因此本人会经常在开发中做一些总结. express获取参数有三种方法:官网介绍如下 Checks route para ...

  5. 01-E-1: 迭代与递归

  6. 使用net Manager工具配置远程连接oracle

    一,在服务端配置oracle端口在命令行中输入netca命令,打开相关配置默认端口号为1521 二,配置端口后使用Telnet工具调试端口是否联通 在命令行输入telnet 服务器ip 端口号 三,找 ...

  7. 用Hash Table(哈希散列表)实现统计文本每个单词重复次数(频率)

    哈希表在查找方面有非常大应用价值,本文记录一下利用哈希散列表来统计文本文件中每个单词出现的重复次数,这个需求当然用NLP技术也很容易实现. 一.基本介绍 1.Hash Key值:将每个单词按照字母组成 ...

  8. Java 设计模式系列(十二)策略模式(Strategy)

    Java 设计模式系列(十二)策略模式(Strategy) 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以 ...

  9. CTF中的变量覆盖漏洞

    https://www.cnblogs.com/bmjoker/p/9025351.html 原   作 者:bmjoker出 处:https://www.cnblogs.com/bmjoker/p/ ...

  10. vue项目 一行js代码搞定点击图片放大缩小

    一行js代码搞定xue项目需要点击图片放大缩小,其实主要用的是用到了vue:class的动态切换,内容比较简单.一开始我把维护的需求想得太复杂了,和测试小姐姐聊了一下才反应过来. 两个月不到跟了四个项 ...