首先在idea的plugin搜索jclasslib-bytecode-viewer并且安装,重启以后,新建一个类
public class CodeTest implements CodeInterface {
private Integer k;
private static String s;
static{
s = "init static";
}
public static void main(String[] args) {
int i = 0 ;
String str = new String("hello world!");
i = 1;
int j = 2;
System.out.println(i);
}
}
点击Build的Build Project, 然后点击View的Show ByteCode with classLib(截图来自IDEA字节码学习查看神器jclasslib bytecode viewer介绍_明明如月的技术博客-CSDN博客), 一定要先Build Project,否则会报错"error reading class file: not found"
然后能得到如下截图,这里面的接口信息,常量池等信息其实就是方法区的一部分
一般信息就是这个类的一些简介、统计,字节码文件其实前面有个魔数,不过我们先从常量池讲起
常量池
常量池存放了类名CodeTest,还有变量名k
还有方法名main及其参数args
而且局部变量名str以及常量字符串"hello word!"都存在常量池
接口
接口就是记录它所实现的接口
字段
从这里来看,只记录了类的成员变量,并没有记录main方法里面的局部变量
方法
可以看到有3个方法 <init> <clinit> <main>, 其中<init>方法等同于类的构造函数,在遇到new指令时,自然会调用该方法,而<clinit>是在有static代码块的时候会自动生成的,主要看下main方法:
右边的字节码就是对应main方法的字节码,
具体来说,Java 字节码中与调用相关的指令共有五种。
invokestatic:用于调用静态方法。
invokespecial:用于调用私有实例方法、构造器,以及使用 super 关键字调用父类的实例方法或构造器,和所实现接口的默认方法。
invokevirtual:用于调用非私有实例方法。
invokeinterface:用于调用接口方法。
invokedynamic:用于调用动态方法。
在编译过程中,我们并不知道目标方法的具体内存地址。因此,Java 编译器会暂时用符号引用来表示该目标方法(比如 invokespecial后面的 #4,符号引用存储在 class 文件的常量池之中)。这一符号引用包括目标方法所在的类或接口的名字,以及目标方法的方法名和方法描述符。
下面说一下LineNumberTable和LocalVariableTable
起始PC其实就是main字节码的行号,然后行号就是main的java代码的行号,比如起始PC 16行为getstatic #5 而java对应的就是14行的System.out.println(i);
至于LocalVariableTable,我们看到的就是main方法里面的局部变量咯,包括main方法参数args也存在局部变量表里面