jvm字节码-clinit及本地变量表
程序员文章站
2022-04-18 14:47:58
...
源文件如下:
public class Test {
final static PersonReq req = new PersonReq();
final static String helloStr = "55hello6";
static String zhangsxan = "zhangsan";
public static void main(String[] args) {
System.out.println(Test.helloStr);
}
}
对应的字节码
1、req是一个常量,但是它是一个对象,所以先放在了cinit里初始化,在类加载的 准备阶段,赋值为null,在初始化阶段进行赋值
2、hellStr是一个常量字符串,它是直接在类加载的准备阶段就赋值成了“55hello6”,如果此类里,没有req,和zhangsan这个变量的话,编译成字符码,的是不会用cinit方法的。
3、zhangsan是一个静态变量,他是类加载的准备阶段赋值为null,在初始化阶段,初始化为“zhangsan”,所以静态变量和对象类型的常量的准备和初始化阶段是一样的,此处和常量字符串及常量基本类型有区别用javap可以看下编译之后的字节码,可以看下上面提到的信息
{
//对象常量只定义
static final com.wangyin.seapay.authentication.business.common.model.request.PersonReq req;
//放在常量池里的常量字符串,在准备阶段已经就绪
static final java.lang.String helloStr;
Constant value: String 55hello6
//静态变量,只定义,赋值
static java.lang.String zhangsxan;
public com.wangyin.seapay.authentication.business.tool.Test();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/wangyin/seapay/authentication/business/tool/Test;
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=1, Args_size=1
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4; //String 55hello6
5: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 13: 0
line 15: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
MethodParameters: length = 0x5
01 00 1B 00 00
//是clinit方法,在这里为req,和zhangsan进行了赋值
static {};
Code:
Stack=2, Locals=0, Args_size=0
0: new #6; //class com/wangyin/seapay/authentication/business/common/model/request/PersonReq
3: dup
4: invokespecial #7; //Method com/wangyin/seapay/authentication/business/common/model/request/PersonReq."<init>":()V
7: putstatic #8; //Field req:Lcom/wangyin/seapay/authentication/business/common/model/request/PersonReq;
10: ldc #9; //String zhangsan
12: putstatic #10; //Field zhangsxan:Ljava/lang/String;
15: return
LineNumberTable:
line 9: 0
line 11: 10
}
4、还有一点,是我们在反编译之后经常看到局部变量名字是var1,var2之类的,总之不是我们之前定义的变量名字,原因是我们在javac时,如果加了 -g:none,就可以忽略所有的调试信息,就会把源码中定义的变量名给丢失,如果没加的话,在反编译时就可以还原变量表,及源码的行数与编译之后的行数对应,就是LineNumberTable(行数应)和LocalVariableTable(本地变量表),
比如源码是
public static void main(String[] args) {
String nihao = "hi";
}
我们看到的反翻译是
public static void main(String[] paramArrayOfString)
{
String str = "hi";
}
局部变量的名字,都变的不认识了,
,但是如果在执行javac时添加了 -g,的话,就是下面的情况了,和源码完全一致了,
public static void main(String[] args)
{
String nihao = "hi";
}
我们看下本地变量表到底长什么样子,用Javap来看的字符码,如下:
public static void main(java.lang.String[]);
Code:
Stack=1, Locals=2, Args_size=1
0: ldc #2; //String hi
2: astore_1
3: return
//行数对应信息,也是为了调试时,可以与源码对应上,如果没有的话,我们在调试时,就与源码的行不是一一对应了
LineNumberTable:
line 5: 0
line 6: 3
//本地变量表,在调试时有用,可以与源码对应的上
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 args [Ljava/lang/String;
3 1 1 nihao Ljava/lang/String;
}
推荐阅读