深入理解Java虚拟机学习之一安装字节码查看工具

写在前边的话


最近在看《深入理解Java虚拟机》这本书,学习Java虚拟机底层实现原理。通过写博客的方式记录自己的学习过程以及对知识的理解。如有总结不正确的地方,欢迎大家指出!


首先学习Java虚拟机的话,肯定是要跟字节码打交道的。那么如何查看字节码文件呢?本篇文章就来总结一下查看字节码的方式。

方式一 使用jdk自带的反解析工具查看Java字节码

一、javap命令简述
javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。

二、使用方法
javap
其中classes就是你要反编译的class文件。

用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?        输出此用法消息
  -version                 版本信息
  -v  -verbose             输出附加信息
  -l                       输出行号和本地变量表
  -public                  仅显示公共类和成员
  -protected               显示受保护的/公共类和成员
  -package                 显示程序包/受保护的/公共类
                           和成员 (默认)
  -p  -private             显示所有类和成员
  -c                       对代码进行反汇编
  -s                       输出内部类型签名
  -sysinfo                 显示正在处理的类的
                           系统信息 (路径, 大小, 日期, MD5 散列)
  -constants               显示最终常量
  -classpath <path>        指定查找用户类文件的位置
  -cp <path>               指定查找用户类文件的位置
  -bootclasspath <path>    覆盖引导类文件的位置

一般常用的是-v -l -c三个选项。
javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。
javap -l 会输出行号和本地变量表信息。
javap -c 会对当前class字节码进行反编译生成汇编代码。

三、例子

rp@MacBook-Pro java % javap -v HelloWorld
Classfile /Users/rp/Documents/java/HelloWorld.class
  Last modified 2020-11-24; size 720 bytes
  MD5 checksum dae445d00ec5ef52e4c5100e05007f53
  Compiled from "HelloWorld.java"
public class HelloWorld
  minor version: 0
  major version: 59
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Fieldref           #8.#9          // java/lang/System.out:Ljava/io/PrintStream;
   #8 = Class              #10            // java/lang/System
   #9 = NameAndType        #11:#12        // out:Ljava/io/PrintStream;
  #10 = Utf8               java/lang/System
  #11 = Utf8               out
  #12 = Utf8               Ljava/io/PrintStream;
  #13 = String             #14            // Hello World!
  #14 = Utf8               Hello World!
  #15 = Methodref          #16.#17        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #16 = Class              #18            // java/io/PrintStream
  #17 = NameAndType        #19:#20        // println:(Ljava/lang/String;)V
  #18 = Utf8               java/io/PrintStream
  #19 = Utf8               println
  #20 = Utf8               (Ljava/lang/String;)V
  #21 = String             #22            // HelloWorld
  #22 = Utf8               HelloWorld
  #23 = Methodref          #16.#24        // java/io/PrintStream.println:(Z)V
  #24 = NameAndType        #19:#25        // println:(Z)V
  #25 = Utf8               (Z)V
  #26 = Methodref          #27.#28        // java/lang/String.equals:(Ljava/lang/Object;)Z
  #27 = Class              #29            // java/lang/String
  #28 = NameAndType        #30:#31        // equals:(Ljava/lang/Object;)Z
  #29 = Utf8               java/lang/String
  #30 = Utf8               equals
  #31 = Utf8               (Ljava/lang/Object;)Z
  #32 = Class              #22            // HelloWorld
  #33 = Utf8               Code
  #34 = Utf8               LineNumberTable
  #35 = Utf8               main
  #36 = Utf8               ([Ljava/lang/String;)V
  #37 = Utf8               StackMapTable
  #38 = Class              #39            // "[Ljava/lang/String;"
  #39 = Utf8               [Ljava/lang/String;
  #40 = Utf8               SourceFile
  #41 = Utf8               HelloWorld.java
{
  public HelloWorld();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=4, args_size=1
         0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #13                 // String Hello World!
         5: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: ldc           #21                 // String HelloWorld
        10: astore_1
        11: ldc           #21                 // String HelloWorld
        13: astore_2
        14: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        17: aload_1
        18: aload_2
        19: if_acmpne     26
        22: iconst_1
        23: goto          27
        26: iconst_0
        27: invokevirtual #23                 // Method java/io/PrintStream.println:(Z)V
        30: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        33: aload_1
        34: aload_2
        35: invokevirtual #26                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
        38: invokevirtual #23                 // Method java/io/PrintStream.println:(Z)V
        41: ldc           #21                 // String HelloWorld
        43: astore_3
        44: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        47: aload_1
        48: aload_3
        49: if_acmpne     56
        52: iconst_1
        53: goto          57
        56: iconst_0
        57: invokevirtual #23                 // Method java/io/PrintStream.println:(Z)V
        60: return
      LineNumberTable:
        line 4: 0
        line 5: 8
        line 6: 11
        line 7: 14
        line 8: 30
        line 9: 41
        line 10: 44
        line 11: 60
      StackMapTable: number_of_entries = 4
        frame_type = 255 /* full_frame */
          offset_delta = 26
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 28
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "HelloWorld.java"

方式二 使用jclasslib插件查看Java字节码

一、IDEA下载与显示jclasslib Bytecode viewer插件:
1、打开IDEA的Settings设置,找到Plugins,然后点击右侧Browse repositories…按钮。
在这里插入图片描述
2、弹出框里搜索jclasslib插件,点击右侧Install按钮,下载完成后点击Restart Intellij IDEA按钮。
在这里插入图片描述
3、重启IDEA后,在View中选择Show Bytecode With jclasslib。
在这里插入图片描述
4、此时右侧会显示出jclasslib插件导航栏,点击展开后即可查看.class内容。
在这里插入图片描述
二、如何使用jclasslib插件查看Java字节码
1、以我的TransferServiceImpl类为例,进行查看Java字节码信息。

package com.fuyf.stu.service.impl;

import com.fuyf.stu.annotation.Autowired;
import com.fuyf.stu.annotation.Service;
import com.fuyf.stu.annotation.Transactional;
import com.fuyf.stu.dao.TransferDao;
import com.fuyf.stu.pojo.Account;
import com.fuyf.stu.service.TransferService;

import java.sql.SQLException;

@Service("transferService")
public class TransferServiceImpl implements TransferService {

    private TransferDao transferDao;
    @Autowired
    public void setTransferDao(TransferDao transferDao){
        this.transferDao = transferDao;
    }

    /**
     * 转账功能实现
     * @param from
     * @param to
     * @param amount
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    @Override
    @Transactional
    public void transfer(Account from,Account to,Double amount) throws SQLException, ClassNotFoundException {

        //转出账户取得金额
        Double amountFrom = transferDao.queryAmountByCardNaAndName(from);
        //转入账户取得金额
        Double amountTo = transferDao.queryAmountByCardNaAndName(to);
        //更新转出账户金额
        from.setMoney(amountFrom-amount);
        transferDao.updateAmount(from);
        int i = 1 / 0;
        //更新转入账户金额
        to.setMoney(amountTo+amount);
        transferDao.updateAmount(to);
    }
}

2、展开jclasslib插件后,选择TransferServiceImpl.java文件中的方法->transfer->Code,即可在右侧查看Bytecode栏的Java字节码信息。
在这里插入图片描述

至此,本篇介绍已经完了。既然知道了字节码查看工具,那么class文件中都有什么内容呢?感兴趣的童鞋可以查看《深入理解Java虚拟机学习之二class文件内容详解》。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值