stringbuilder class再反编译_JVM篇(02.class字节码解析)
class文件格式概述
我们先看下classFile的结构(查阅java 虚拟机规范)
ClassFile{ u4 magic;CA为一组,1111 u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 infterfaces_count; u2 infterfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_couunt]; u2 attributes_count; attribute_info attributes[attributes_count]; }在ClassFile结构中,各项的含义描述如下:magic(魔数)minor_version(副版本号)、major_version(主版本号)constant_pool_count(常量池计数器)constant_pool[](常量池)access_flag(访问标识)this_class(类索引)super_class(父类索引)infterfaces_count(接口计数器)infterfaces[](接口表)fields_count(字段计数器)fields[](字段表)methods_count(方法计数器)methods[](方法表)attributes_count(属性计数器)attributes[](属性表)
二进制字节码如下:
Class文件是一组以8字节为单位的字节流,各个数据项目按顺序紧凑排列(如上图开头的字符CA为一组,CA用二进制表示为1100 1010,共8字节)
对于占用空间大于8字节的数据项,按照高位在前的方式分割成多个8字节进行存储
Class文件格式里面只有两种类型:无符号数、表
无符号数:基本数据类型,以u1、u2、u4、u8来代表几个字节的无符号数(如classfile第一行的u4 magic;对应二进制字节码的CA FE BA BE)
表:由多个无符号数和其他表构成的复合数据类型,通常以"_info"结尾
阅读class文件
创建如下代码:
public class Test { private static String params = "Hello World"; public static void main(String[] args) { System.out.println("params = " + params); }}
通过反编译(javap)查看生成的class文件:
Classfile /F:/crazy/freedom/ifd-jvm/target/classes/cn/crazy/dreamer/Test.class Last modified 2020-10-12; size 861 bytes MD5 checksum 03940161a851df384ccc9ab26db9046c Compiled from "Test.java"public class cn.crazy.dreamer.Test minor version: 0 major version: 52 // 与jdk有关 flags: ACC_PUBLIC, ACC_SUPER // 访问标识public,superConstant pool: // 常量池 #1 = Methodref #12.#30 // java/lang/Object."":()V #2 = Fieldref #31.#32 // java/lang/System.out:Ljava/io/PrintStream; #3 = Class #33 // java/lang/StringBuilder #4 = Methodref #3.#30 // java/lang/StringBuilder."":()V #5 = String #34 // params = #6 = Methodref #3.#35 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #7 = Fieldref #11.#36 // cn/crazy/dreamer/Test.params:Ljava/lang/String; #8 = Methodref #3.#37 // java/lang/StringBuilder.toString:()Ljava/lang/String; #9 = Methodref #38.#39 // java/io/PrintStream.println:(Ljava/lang/String;)V #10 = String #40 // Hello World #11 = Class #41 // cn/crazy/dreamer/Test #12 = Class #42 // java/lang/Object #13 = Utf8 params #14 = Utf8 Ljava/lang/String; #15 = Utf8 #16 = Utf8 ()V #17 = Utf8 Code #18 = Utf8 LineNumberTable #19 = Utf8 LocalVariableTable #20 = Utf8 this #21 = Utf8 Lcn/crazy/dreamer/Test; #22 = Utf8 main #23 = Utf8 ([Ljava/lang/String;)V #24 = Utf8 args #25 = Utf8 [Ljava/lang/String; #26 = Utf8 MethodParameters #27 = Utf8 #28 = Utf8 SourceFile #29 = Utf8 Test.java #30 = NameAndType #15:#16 // "":()V #31 = Class #43 // java/lang/System #32 = NameAndType #44:#45 // out:Ljava/io/PrintStream; #33 = Utf8 java/lang/StringBuilder #34 = Utf8 params = #35 = NameAndType #46:#47 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #36 = NameAndType #13:#14 // params:Ljava/lang/String; #37 = NameAndType #48:#49 // toString:()Ljava/lang/String; #38 = Class #50 // java/io/PrintStream #39 = NameAndType #51:#52 // println:(Ljava/lang/String;)V #40 = Utf8 Hello World #41 = Utf8 cn/crazy/dreamer/Test #42 = Utf8 java/lang/Object #43 = Utf8 java/lang/System #44 = Utf8 out #45 = Utf8 Ljava/io/PrintStream; #46 = Utf8 append #47 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #48 = Utf8 toString #49 = Utf8 ()Ljava/lang/String; #50 = Utf8 java/io/PrintStream #51 = Utf8 println #52 = Utf8 (Ljava/lang/String;)V{ public cn.crazy.dreamer.Test(); // 无参构造 descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 11: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcn/crazy/dreamer/Test; public static void main(java.lang.String[]); // main方法 descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."":()V 10: ldc #5 // String params = 12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: getstatic #7 // Field params:Ljava/lang/String; 18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 24: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 27: return LineNumberTable: line 16: 0 line 17: 27 LocalVariableTable: Start Length Slot Name Signature 0 28 0 args [Ljava/lang/String; MethodParameters: Name Flags args static {}; // 静态变量初始化,在jvm静态变量初始化都是通过静态块实现 descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: ldc #10 // String Hello World 2: putstatic #7 // Field params:Ljava/lang/String; 5: return LineNumberTable: line 13: 0}SourceFile: "Test.java"
我们通过反编译生成的classFile是jdk工具将二进制的.class文件根据java虚拟机规范解析成我们开发人员比较易于阅读的格式。javap工具生成非正式的"虚拟机汇编语言",格式如下:[[...]][]
是指令操作码在数组中的下标,该数组以字节形式来存储当前方法的java虚拟机代码;也可以是相对于方法起始处的字节偏移量(对应上面代码中的 0)
是指令的助记码(对应上面代码中的 ldc)
是操作数(对应上面代码中的 #10)
是行尾的注释(对应上面代码中的 // String Hello World)
阅读class文件时需要注意:
constant_pool_count:是从1开始的
不同的常量类型,用tag来区分的,它后面对应的info结构是不一样的
L表示对象,[表示数组、V表示void
stack:方法执行时操作栈的深度
locals:局部变量所需的存储空间,单位是slot(slot是虚拟机为局部变量分配内存所使用的最小单位)
args_size:参数个数,为1的话,因实例方法默认会传入this,local也会预留一个slot来存放
以如下classfile为例,我们大概来阅读下:
static {}; descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: ldc #10 // String Hello World 2: putstatic #7 // Field params:Ljava/lang/String; 5: return LineNumberTable: line 13: 0 ldc #10:装载常量hello world(#10表示在常量池中的编号)putstatic #7:赋值给params
我们粗略阅读下main方法反编译生成的代码:
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."":()V 10: ldc #5 // String params = 12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: getstatic #7 // Field params:Ljava/lang/String; 18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 24: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 27: return LineNumberTable: line 16: 0 line 17: 27 LocalVariableTable: Start Length Slot Name Signature 0 28 0 args [Ljava/lang/String; MethodParameters: Name Flags args getstatic #2:拿到PrintStreamnew #3:创建stringbuild对象dup:复制invokespecial #4:调用初始化方法初始化stringbuild对象ldc #5:装载常量invokevirtual #6: 调用方法getstatic #7:获取静态变量
使用IDEA执行javap
每次执行javap反编译命令时,都要进入.class文件的目录,相对来说比较麻烦,我们可以通过idea来运行javap异常点击"Settrings"->"Tools"->"External Tools",点击"+"添加一个外部工具
进入编辑页面,按如图所示进行配置:
在java文件上点击右键(前提是java文件已经编译完成),按如图所示进行操作,即可在idea命令窗口输出class信息。
class字节码规范在java虚拟机规范中有详细描述,建议到官网中下载官方文档进行阅读(https://docs.oracle.com/javase/specs/index.html),也可以购买JVM方面的书籍(Java 虚拟机规范[爱飞翔 周志明 等译])
stringbuilder class再反编译_JVM篇(02.class字节码解析)相关推荐
- 反编译工具的使用和字节码文件的查看(Binary Viewer)
反编译工具的使用和字节码文件的查看 什么是反编译 反编译工具 链接: https://pan.baidu.com/s/15r-Qg5_wOhzO8fKRso3x8A 提取码: lmm7 –来自百度网盘 ...
- 反编译python 生成的exe源码
反编译python 生成的exe源码 记录反编译exe工具使用 工具准备 – pyinstxtractor.py – uncompyle6 – sublime Text(或者其他的二进制编辑工具) 一 ...
- exe反编译为pyc,再反编译为py
第一步反编译:exe到pyc 参考这篇文章,写的很好:有个注意点就是pyc文件必须加文件头. pyinstaller打包的.exe文件反编译成.py文件 - no1r - 博客园 (cnblogs.c ...
- 反编译获取任何小程序源码——看这篇就够了
一 准备工具 1 node.js 运行环境 下载地址:Node.js 2 反编译的脚本 源码链接:https://download.csdn.net/download/wanlitengfei/867 ...
- 反编译获取微信小程序源码(包含错误解决办法)
PS:本文章仅用于分享自己反编译的过程以及解决办法,切勿小程序反编译成功后做一些违反人伦的事情! 一. 前言 最近在搞学校的一个晚签到小程序的时候遇到了一个MD5加密问题,试了好多方法都没有解决,所以 ...
- 安卓模拟器反编译获取微信小程序源码。
认识一下.wxapkg文件 先来想想一个很简单的问题,小程序的源文件存放在哪? ● 当然是在微信的服务器上. ● 但是在微信服务器上,普通用户想要获取到,肯定是十分困难的,有没有别的办法呢? ● 简单 ...
- Eclipse中Jar包的反编译(通过jar包查看源码)
很多时候我们在公司工作时,在Eclipse引用其他人封装好的jar包时是看不到源码的,这样一来就不能很好得理解他人的意图,于是就需要借助反编译的帮助了.好了,下面是反编译的正确使用方法. 一.下载ne ...
- eclipse安装反编译插件:jd-eclipse 查看源码
在我们使用第三方库的时候,使用了里面的方法,想点进去看一下源码,这个时候就需要安装一个反编译插件了 1.下载jd-eclipse-2.0.0.zip文件 去官网http://java-decompil ...
- IDEA反编译出整个jar包源码
1.idea需要安装 Java Bytecode Decomplier 插件 会在idea安装路径里多个 java-decompiler.jar E:\JetBrains\IntelliJ IDEA ...
最新文章
- (转)java redis使用之利用jedis实现redis消息队列
- [linux驱动]linux驱动模块
- 固定宽度弹性布局(以适应各种各辨率)
- 把所有圆圈连接起来的游戏_20个幼儿园体育小游戏教程
- Oracle Study之案例--安装Oracle内核参数配置
- hibernate笔记--组合主键映射方法
- 有效压缩量子数据的量子自动编码器——Quantum autoencoders for efficient compression of quantum data论文翻译
- 机器学习-随机森林算法
- 论文Re-ranking Person Re-identification with k-reciprocal Encoding(person re-id的re-ranking)
- 【专家访谈】疫情带来的商机风口,汽车零部件企业如何抓住机遇实现华丽转身?
- android xml pid vid,关于Android机型的pid vid的那些破事儿
- 精讲Spring Boot—入门+进阶+实例
- Problem D: 字符构成的图形
- 数字IC面试高频考点之跨时钟域信号处理
- 前端性能优化(图片优化)
- IDEA 如何 buil dpath
- Rabbit and Grass --- 组合博弈 GS函数 记录
- Windows 端口被占用解决方案
- EF 框架的简介、发展历史;ORM框架概念
- uniapp + uniCloud+unipay 实现微信小程序支付功能