欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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;


}

 

相关标签: java