zoukankan      html  css  js  c++  java
  • Java字节码常量池深度剖析与字节码整体结构分解

    常量池深度剖析:

    在上一次【https://www.cnblogs.com/webor2006/p/9416831.html】中已经将常量池分析到了2/3了,接着把剩下的分析完,先回顾一下我们编译的源文件为:

    然后用javap -verbose查看一下编译字节码的信息,其中字符串相关的如下:

    而对应用Hex Fiend来查看字符码的二进制文件的位置如下:

    另外在继续分析之前再来回顾下常量的对应表,如下:

    好下面开始,先来读一个字节来看一下是什么类型的常量:

    查表可以看到是属于这个常量:

    接着2个字节表示字符串的长度,所以往下数二个字节:

    长度为4,则下往下数4个字节则为常量的内容:

    用javap -verbose来确认一下是否也是它:

    接下来继续读一个字节:

    又是同样的常量类型,所以直接再读二个字节来看一下字符串的长度是多少:

    长度为3,则往后再数3个字节:

    看一下javap -verbose:

    实际上"getA()I"就可以确认其方法名为getA,无参,并且返回值为整型,就可以完全的对应的源程序中的方法了。

    接下来继续往下,读一个字节:

    同样的类型,不多说,直接往下再看两个字节来决定字符串的长度:

    占四个字节,于是乎往后再数四个字节:

    对一下javap -verbose:

    继续往下,读一个字节:

    再数2个字节:

    往下数四个字节:

    对应于javap -verbose:

    而同样的“setA(I)V”,表示方法名为setA,方法的参数为整型,无返回值,这样又可以定位到具体的唯一的方法了。

    继续往下:

    往下数10个字节:

    对应javap -verbose:

    代表源文件,再往下读:

    长度为12,往下数12个字节:

    而这两个信息描述了当前字节码文件是由哪个源文件编译出来的,所以这也是为啥在执行javap命令时有如下一个信息:

    继续往下走:

    此时不再是01类型的常量了,而是12,所以具体它代表什么类型还得查表,如下:

    其第二项表示名称的索引,而第三项为描述的索引,所以往下读4个字节:

    看一下javap -verbose所显示的:

    其中方法名称为<init>表示是构造方法,而()V表示该构造方法不带参数没的返回值,也就是默认构造方法。

    接下来继续:

    又是同样的常量,所以直接往后数四个字节:

    对应javap -verbose:

    这个信息表示成员变量a,如下:

    继续往下看:

    字符串常量,对它的分析已经了如指掌了,往后数两个字节来看下字符串的长度:

    长度为24,则往后数24个字节:

    对应javap -verbose:

    表示类的全局限定名,注意反应到字节码文件来说全局限定名都是以“/”分隔的,而不像我们看到的包名那样以“.”分隔的,继续往下:

    再往下数两个字节:

    16,则往下数16个字节:

    对应javap -verbose:

    表示当前类的父类的完全限制名,到此,常量池就全部分析完了~所以总结一下,对于一个类常量池的大小是不定的,那JVM如何在字体码文件中来知道常量池在哪结束呢?首先字符码能知道常量池的总大小,如下:

    为24个,但是由于第一个为备用的,所以总常量池的大小为23,而每个常量第一个字节都是什么类型的常量,然后不同的常量其往下读几个字节都是确定的,所以这样就可以知道读到哪常量池就结束了。

    字节码整体结构分解:

    上面已经将整个常量池都已经分析完了,那之后还有那么多字节:

    对应javap -verbose:

    Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/jvm_lectue/out/production/classes/com/jvm/bytecode/MyTest1.class
      Last modified Aug 10, 2018; size 479 bytes
      MD5 checksum 4616561f95c24d6b04ea48a360437b8d
      Compiled from "MyTest1.java"
    public class com.jvm.bytecode.MyTest1
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #4.#20         // java/lang/Object."<init>":()V
       #2 = Fieldref           #3.#21         // com/jvm/bytecode/MyTest1.a:I
       #3 = Class              #22            // com/jvm/bytecode/MyTest1
       #4 = Class              #23            // java/lang/Object
       #5 = Utf8               a
       #6 = Utf8               I
       #7 = Utf8               <init>
       #8 = Utf8               ()V
       #9 = Utf8               Code
      #10 = Utf8               LineNumberTable
      #11 = Utf8               LocalVariableTable
      #12 = Utf8               this
      #13 = Utf8               Lcom/jvm/bytecode/MyTest1;
      #14 = Utf8               getA
      #15 = Utf8               ()I
      #16 = Utf8               setA
      #17 = Utf8               (I)V
      #18 = Utf8               SourceFile
      #19 = Utf8               MyTest1.java
      #20 = NameAndType        #7:#8          // "<init>":()V
      #21 = NameAndType        #5:#6          // a:I
      #22 = Utf8               com/jvm/bytecode/MyTest1
      #23 = Utf8               java/lang/Object
    {
      public com.jvm.bytecode.MyTest1();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: iconst_1
             6: putfield      #2                  // Field a:I
             9: return
          LineNumberTable:
            line 3: 0
            line 4: 4
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      10     0  this   Lcom/jvm/bytecode/MyTest1;
    
      public int getA();
        descriptor: ()I
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: getfield      #2                  // Field a:I
             4: ireturn
          LineNumberTable:
            line 7: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/jvm/bytecode/MyTest1;
    
      public void setA(int);
        descriptor: (I)V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0
             1: iload_1
             2: putfield      #2                  // Field a:I
             5: return
          LineNumberTable:
            line 11: 0
            line 12: 5
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       6     0  this   Lcom/jvm/bytecode/MyTest1;
                0       6     1     a   I
    }
    SourceFile: "MyTest1.java"

    目前还分析不了,因为还缺少知识理论,所以先来补一补知识,先来对字节码的整体结构有一个了解,先来看张图:

    目前已经学习了前三个结构,如下:

    接着来了解新的字节结构,接下来是“Access Flags”,表示访问修饰符:

    如:public、public static、public abstract、private、protected等。 

    接着往下表示当前类的名字,如下:

    再往下表示父类的名字:

    接下来表示接口相关的信息:

    其中可以发现一个细节:

    父类的字节数是确定的,而接口是不确实的,这也跟java的单继承多实现的特性吻和。

    继续往下则是字段相关的信息:

    接下来由是类的方法相关的一些信息:

    这个就比较复杂了,因为方法里面有执行代码,在未来会学习到。

    最后则表示当前类的一些附加的属性:

    因为JVM在编译时会增加一些特定的一些属性信息。

  • 相关阅读:
    用javafx webview 打造自己的浏览器
    用cef Python打造自己的浏览器
    无需人工智能和机器学习,实现基于手势识别的计算器
    经常抱怨在公司学不到技术,学的技术没有使用场景怎么破?
    Linux相关集合
    个人博客注册,申请,美化流程
    Django注意知识点(二)
    Django 注意知识点(一)
    Java学习笔记(四)
    Java学习笔记(三)
  • 原文地址:https://www.cnblogs.com/webor2006/p/9457722.html
Copyright © 2011-2022 走看看