Android学习笔记 手动执行Java字节码

Android学习笔记 手动执行Java字节码

@[Android, 字节码]

背景

这一次是玩安卓知识星球的第二期作业(分析每一行字节码执行过程中的本地变量数组和操作数栈的状态),完成第一期作业的过程中需要需要操作字节码,但借助Javassist的帮助也没有去学习字节码的相关知识,现在不得不好好的看看Java的字节码究竟是怎样执行的。

JVM的内存模型

首先复习一下JVM的内存模型 ,JVM的内存模型分为几个区域,如下图:


JVM的内存模型.jpg
  • 程序计数器:这是线程私有的数据区,用来表示当前执行的是哪一行的指令
  • 虚拟机栈:一个栈结构,存储的元素被称为栈帧,每个栈帧包含了一个方法的数据和部分过程结果的区域,同时包含动态连接的信息和方法返回值
    • 局部变量表:用来保存方法局部变量的数组结构
    • 操作数栈:JVM是基于栈结构,操作数栈就是具体用辅助执行指令的栈,栈深度在编译期就写入到class文件中
    • 动态链接:作用是将对方法符号引用转换为直接引用
    • 返回地址:方法执行完成后返回的位置,即调用自己的位置
  • 本地方法栈:跟虚拟机栈类似的为本地方法服务的栈结构
  • 方法区:线程共享的区域,存储运行时常量池,方法的字节码数据,类的字段等
  • 堆:也是线程共享的区域,存储的是Java动态生成的对象数据,垃圾回收器管理的也是这的区域

class文件

对JVM内存模型有了个简单的回顾,我们继续看看class文件的结构是怎么样的。


1986868-5b44e95ff7e30847.jpeg
  1. 魔数和版本号都是对文件本身的描述
  2. 常量池:存储的是字面量和符号引用,除了对一些基本类型的操作,像是类名、方法名、常量等都在这里有定义
  3. 访问标记:标记class是表示一个类还是一个接口,是否公开,是否是abstract等信息
  4. 类索引:引用常量池中的类的全限定类名
  5. 父类索引:引用常量池中的父类的全限定类名
  6. 接口索引:接口数量和引用常量池中的接口的全限定接口名
  7. 字段表集合:定义类所有字段的各种属性
  8. 方法:包含每个方法的各种属性,其中就包括上面说到的操作数栈的深度
  9. 属性:定义了class文件的属性,比如源代码文件名

手动执行字节码指令

接下来结合代码,我们来手动执行字节码指令:

public class Hello {
    public static void main(String[] args) {
        int a = 1;
        int b = 10;
        String c = "abc";
        System.out.println(a + b + c + get3());
    }

    private static int get3() {
        int result = 0;
        for (int i = 0; i < 3; i++) {
            result ++;
        }
        return result;
    }
}

// 生成的字节码指令
// main()
 0 iconst_1
 1 istore_1
 2 bipush 10
 4 istore_2
 5 ldc #2 
 7 astore_3
 8 getstatic #3 
11 new #4 
14 dup
15 invokespecial #5 >
18 iload_1
19 iload_2
20 iadd
21 invokevirtual #6 
24 aload_3
25 invokevirtual #7 
28 invokestatic #8 
31 invokevirtual #6 
34 invokevirtual #9 
37 invokevirtual #10 
40 return

// get3()
 0 iconst_0
 1 istore_0
 2 iconst_0
 3 istore_1
 4 iload_1
 5 iconst_3
 6 if_icmpge 18 (+12)
 9 iinc 0 by 1
12 iinc 1 by 1
15 goto 4 (-11)
18 iload_0
19 ireturn

代码很简单,我们先来看以下各部分在虚拟机中的存储情况


Java字节码总览 | center

代码加载到方法区,正在被执行的方法作为栈帧被初始化到虚拟机栈,执行第一条指令,常量1被压入栈


1576237431800.png

new对象时,在堆生成该对象,并将对象的地址压入栈
1576237550745.png

执行方法时,调用方法生成新的栈帧并压入虚拟机栈,然后将返回值设为方法调用处的下一条指令


1576237607977.png

退出方法时从返回地址返回,如果有返回值则将栈顶的值压入调用处的操作数栈
1576237726715.png

以上只是部分分析过程,具体可以下载我做的ppt查看全部的过程会更清楚
class执行过程.gif

ppt的地址:

链接:https://pan.baidu.com/s/11SDZbSAiiEGtlg5wZi1ztw
提取码:np8f

总结

将学习的结果写出来真的花时间花精力,但是我相信通过这样的方式我会对所学的内容理解更加深刻。现在希望能继续坚持下去。可以参考项目地址

参考:

  • Java虚拟机规范_SE8版
  • 一文让你明白Java字节码
  • Java指令集
  • Java编译原理--运行时栈帧结构

你可能感兴趣的:(Android学习笔记 手动执行Java字节码)