• Java字节码文件结构剖析


    今天起开启JVM的新的知识学习篇章----Java的字节码,那学习Java字节码有啥用呢?我们知道Java是跨平台的一门语言,编写一次到处运行,而支撑着这个特性的根基为两点:JVM和.class字节码文件,java在不同的平台上编译生成的.class文件是一模一样的,而JVM针对不同的操作系统会有不同的JVM,也就是JVM其实并非是跨平台的,而生成的.class文件其实是一个二进制文件,该文件并非是给人看的,而是给机器看的,所以肯定是看不懂的,但是针对字节码JVM是有规范定义的,而如今基于JVM的语言也越来越多,如:scala,groovy,kotlin等,虽说语言不同但是编译出来的字节码文件都是符合JVM规范的,也就是对于JVM来说不管咱们用的是哪种语言编写的,只关心字节码文件是否符合字节码的规范,只要符合了就可以加载运行。另外在了解了字节码文件之后,对于很多深层次的东东【如synchronized关键字、volatile关键字】就会了解得更加透彻了,当然啦,学习它当然是非常之枯燥的,但是这个枯燥是为了让自己学得更加的通透,一切都是值得的,所以下面硬着头皮开始:

    先编写一个简单的代码作为字节码学习的开端,如下:

    然后编译生成字节码文件:

    然后双击打开它:

    【提示】:当我们完全搞懂了字节码的东东之后,也就搞明白了这些反编译工具的运行机制了,为什么从.class文件能过翻译成源代码呢?

    可以看到该反编译还是挺强大的,几乎跟源代码看到的是一样的,这个对于咱们来观察字节码文件不太方便,所以换用java命令来查看一下,这里用javap命令,如下:

    貌似用这个命令执行之后看到的信息没有啥价值,所以这里加一个参数再来看:

    xiongweideMacBook-Pro:classes xiongwei$ pwd
    /Users/xiongwei/Documents/workspace/IntelliJSpace/jvm_lectue/out/production/classes
    xiongweideMacBook-Pro:classes xiongwei$ javap -c com/jvm/bytecode/MyTest1
    Compiled from "MyTest1.java"
    public class com.jvm.bytecode.MyTest1 {
      public com.jvm.bytecode.MyTest1();
        Code:
           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
    
      public int getA();
        Code:
           0: aload_0
           1: getfield      #2                  // Field a:I
           4: ireturn
    
      public void setA(int);
        Code:
           0: aload_0
           1: iload_1
           2: putfield      #2                  // Field a:I
           5: return
    }

    另外还有一个参数可以看到更加详细的字节码信息,如下:

    xiongweideMacBook-Pro:classes xiongwei$ pwd
    /Users/xiongwei/Documents/workspace/IntelliJSpace/jvm_lectue/out/production/classes
    xiongweideMacBook-Pro:classes xiongwei$ javap -c com/jvm/bytecode/MyTest1
    Compiled from "MyTest1.java"
    public class com.jvm.bytecode.MyTest1 {
      public com.jvm.bytecode.MyTest1();
    xiongweideMacBook-Pro:classes xiongwei$ pwd
    /Users/xiongwei/Documents/workspace/IntelliJSpace/jvm_lectue/out/production/classes
    xiongweideMacBook-Pro:classes xiongwei$ javap -verbose com/jvm/bytecode/MyTest1
    Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/jvm_lectue/out/production/classes/com/jvm/bytecode/MyTest1.class
      Last modified Aug 2, 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"

    下面大概来看一下打印的信息:

    好,上面大致分析一下,反正目前也看不懂,接下来用二进制的分析工具来查看一下该字节码文件,有个感性的认识,在mac平台上推荐使用hexfiend,官网:http://ridiculousfish.com/hexfiend/ 

    下载安装好既可,打开它:

    将咱们的字节码文件用它打开:

    目前显示的是四个字节进行分隔的:

    为了更于未来的学习,将其设置为一个字节一显示,如下:

    那对于这一串十六进制的魔鬼数字怎么就可以对应到javap命令所看到的结果那样呢?其实是有一定的规则的,下面先来说一下其规则:

    1、使用javap -verbose命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法、类中的方法信息、类变量与成员变量等信息。

    2、魔数:所有的.class字节码文件的前4个字节都是魔数,魔数值为固定值:0xCAFEBABE。

    回到咱们的二进制中来看是不是这样:

    搜嘎,完来是这么回事,如果一个字节码文件不是这个魔数开头的话JVM则会认为是一个不合法的字节码文件。那问题来了,为啥要以“CAFEBABE”开头呢?CAFE是“咖啡”的意思,而BABE是“宝贝”的意思,翻译过来就是“咖啡宝贝”,而java本来就是咖啡的意思啦,而有个细节可以看到:

  • 相关阅读:
    JS函数节流
    JS中多种方式创建对象
    javascript的几种继承
    多进程基本概念
    APUE(1)——UNIX基本概念
    pthread
    使用TortoiseGit,设置ssh方式连接git仓库。
    mac系统下用ssh方式连接git仓库
    webstorm2017.02版本如何使用material theme
    谷歌浏览器的字体问题
  • 原文地址:https://www.cnblogs.com/webor2006/p/9404249.html
Copyright © 2020-2023  润新知