java 字节码

一、基本介绍

1.1、java的平台无关性

image.png
  • JAVA源代码->Class字节码->JVM解
    释执⾏(依赖于不同的jvm实现跨平台)
  • Java 虚拟机(JVM):负责将字节码⽂
    件翻译成特定平台下的机器码然后运
    ⾏。
  • 不同的平台有不同的JVM实现

1.2、字节码的语言无关性

字节码(ByteCode)是构成平台无关性的基石,这也决定了,只要是能转换成字节码,其他语言也可以运行在jvm之上,所以现在不只是java,还有kotlin、groovy等都可以运行在jvm之上。


image.png

所以把JVM(Java Virtual Machine Java虚拟机)叫CVM(Class Virtual Machine Class虚拟机)反而更合适一些.

1.3、Class文件格式

Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中。

1.3.1、字节码的两种基本数据类型

Class文件结构包含两种数据类型:


image.png
  • 无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
  • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表

1.3.2、Class文件表构成

Class文件格式

1.3.3、编译测试

编译下面这个类

public class JavaCodeTest {}

然后把得到的.class用16进制编辑器直接打开字节码显示是这样的:


可以看到开头的四个字节是CAFEBABE,后面的字节可以意思对应上方表格进行查看,都是严格按照顺序一一对应的。

1.4、常用属性介绍

在Class文件、字段表、方法表都可以携带自己的属性表集合,以用于描述某些场景专有的信息。任何人实现编译器都可以向属性表中写入自己自定义的属性信息,但是java虚拟机运行时会忽略掉他不认识的属性。
《Java虚拟机规范(Java SE 7)》版中,预定义属性已经增加到21项,如下表所示:


虚拟机规范预定义的属性

1.4.1、Code属性

Java程序方法体中的代码经过Javac编译器处理后,最终变为字节码指令存储在Code属性内。Code属性出现在方法表的属性集合之中,但并非所有的方法表都必须存在这个属性,譬如接口或者抽象类中的方法就不存在Code属性。
Code属性是Class文件中最重要的一个属性,如果把一个Java程序中的信息分为代码(Code,方法体里面的Java代码)和元数据(Metadata,包括类、字段、方法定义及其他信息)两部分,那么在整个Class文件中,Code属性用于描述代码,所有的其他数据项目都用于描述元数据。了解Code属性是学习关于字节码执行引擎内容的必要基础,能直接阅读字节码也是工作中分析Java代码语义问题的必要工具和基本技能。
比如这个类

public class JavaCodeTest {
    private int a;

    public int testAdd() {
        return a + 1;
    }
}

编译后的方法部分如下所示(下面这种为转义过的,未转义的都如上面介绍的那样,为16进制形式)
如下所示,具体每一个字段的释义可以参考注释。

{
//---略
  public com.canzhang.asmdemo.test.JavaCodeTest();
    //descriptor 对方法参数和返回值进行描述
    descriptor: ()V//`()`表示无参数,`V`表示Void,无返回值
    flags: ACC_PUBLIC//方法修饰符,表示是public的,可以有多个。
    Code://方法体
      stack=1, locals=1, args_size=1//参数个数
          //aload_0指令表示:将第0个Slot中为reference类型的本地变量推送到操作数栈顶。
         0: aload_0
          //invokespecial指令表示:以栈顶的reference类型的数据所指向的对象作为方法接收者,调用此对象的实例构造器方法
         1: invokespecial #1  // 这里的`#1`是invokespecial指令的参数,表示指向常量池声明的常量:Method java/lang/Object."<init>":()V
         //指令return,含义是返回此方法,并且返回值为void。这条指令执行后,当前方法结束。
         4: return
      //LineNumberTable 是用来描述Java源码行号与字节码行号(字节码偏移量)之间的对应关系,可以配置不生成,不生成就无法获取异常发生源码行号,也无法按照源码的行数来调试程序。
      LineNumberTable:
        line 6: 0

  public int testAdd();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field a:I
         4: iconst_1
         5: iadd
         6: ireturn
      LineNumberTable:
        line 10: 0
}

//---略

疑问点:

  • 第一个方法<init>()是什么?
    java在编译之后会在字节码文件中生成<init>方法,称之为实例构造器
  • 实例构造器<init>()和testAdd(),这两个方法很明显都是没有参数的,为什么Args_size会为1?而且无论是在参数列表里还是方法体内,都没有定义任何局部变量,那Locals又为什么会等于1?

在任何实例方法里面,都可以通过“this”关键字访问到此方法所属的对象。这个访问机制对Java程序的编写很重要,而它的实现却非常简单,仅仅是通过Javac编译器编译的时候把对this关键字的访问转变为对一个普通方法参数的访问,然后在虚拟机调用实例方法时自动传入此参数。因此在实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量,局部变量表中也会预留出第一个Slot位来存放对象实例的引用,方法参数值从1开始计算。这个处理只对实例方法有效,如果testAdd()方法声明为static,那Args_size就不会等于1而是等于0了。

附录:

生成字节码样例参考

java文件

public class JavaCodeTest {
    private int a;

    public int testAdd() {
        return a + 1;
    }
}

//生成字节码
 javac /Users/canzhang/AndroidStudioProjects/ASMDemo/app/src/main/java/com/canzhang/asmdemo/test/JavaCodeTest.java 
//反编译字节码
 javap -v  /Users/canzhang/AndroidStudioProjects/ASMDemo/app/src/main/java/com/canzhang/asmdemo/test/JavaCodeTest.class

反编译结果

Classfile /Users/canzhang/AndroidStudioProjects/ASMDemo/app/src/main/java/com/canzhang/asmdemo/test/JavaCodeTest.class
  Last modified 2020-11-5; size 311 bytes
  MD5 checksum 985828dc144886121bd06e4d19423d65
  Compiled from "JavaCodeTest.java"
public class com.canzhang.asmdemo.test.JavaCodeTest
  minor version: 0//jdk次版本号
  major version: 52//jdk主版本号
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool://常量池
   #1 = Methodref          #4.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#16         // com/canzhang/asmdemo/test/JavaCodeTest.a:I
   #3 = Class              #17            // com/canzhang/asmdemo/test/JavaCodeTest
   #4 = Class              #18            // java/lang/Object
   #5 = Utf8               a
   #6 = Utf8               I
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code//属性的名称 对于每个属性,它的名称需要从常量池中引用一个CONSTANT_Utf8_info类型的常量来表示
  #10 = Utf8               LineNumberTable//属性的名称
  #11 = Utf8               testAdd
  #12 = Utf8               ()I
  #13 = Utf8               SourceFile
  #14 = Utf8               JavaCodeTest.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = NameAndType        #5:#6          // a:I
  #17 = Utf8               com/canzhang/asmdemo/test/JavaCodeTest
  #18 = Utf8               java/lang/Object
{
  public com.canzhang.asmdemo.test.JavaCodeTest();
    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 6: 0

  public int testAdd();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field a:I
         4: iconst_1
         5: iadd
         6: ireturn
      LineNumberTable:
        line 10: 0
}

常用的几个指令

  • .java--->.class: javac /xxxx/JavaCodeTest.java
  • .class 转换字节码内容:javap -c /xxxx/JavaCodeTest.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>    覆盖引导类文件的位置

对应android javac,并不能保证所有都正常编译,因为有很多android sdk的内容是识别不了的,另外android编译过程中也会有自己一些额外处理(比如脱糖、插桩一类的),所以最好直接使用android工程编译后的产物也进行字节码分析,一般工程如下图目录已经存在了编译后的.class文件,我们可以直接取用.class 使用javap转换成可以读懂的文字内容就可以分析了。
具体目录:app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/xxx

image.png

android studio 便捷命令配置

为了方便我们使用javap,可以在android studio 配置tool,方便我们使用:


配置引导

如图示 依次打开 tools-external tools -点击左下角的添加按钮,按照箭头输入几个关键项就可以了:

  • Name:随便填写
  • Program:$JDKPath$\bin\javap
  • Arguments:-c $FileClass$
  • Working directory:$OutputPath$
    然后点击 ok apply 就可以使用了,使用方法,如下图所示:
    image.png

注意事项:

  • 使用的时候默认获取的是当前打开的类作为输入入参,比如你想看MainActivity.java对应的字节码文件,那么就打开MainActivity.java就可以了
  • 需要工程编译后才能按照配置的路径,找到对应的.class文件。
  • 如果编译了,依然提示文件不存在,上面的配置项可以尝试清空,使用右侧的 insert 去插入对应路径,上面的几个路径,都是有选项可选的,按照名字选择即可。

字节码查看器

Hex Fiend
下载后直接双击打开,就可以看到对应的字节码,选中还可以高亮对应。

image.png

未完待续.....

参考文章

kotilin中的某些特性 :https://juejin.im/post/6844903588716609543
反射原理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容