zoukankan      html  css  js  c++  java
  • JVM学习笔记(一、Class文件结构)

    问题:

    1、是不是只有Java编译器才能完成Java到class字节码文件的编译过程?

    不是,像Java/JRuby/Groovy等程序都可以通过自己的编译器转成字节码(class)文件,然后交给JVM。

    2、什么是u1、u2、u4、u8。

    • u1:一个子字节。
    • u2:两个子字节。
    • u4:四个子字节。
    • u8:八个子字节。

    Class文件组成内容:

    虚拟机指令、符号表、其它复制信息(这三个都由两种数据结构组成,无符号数、表)。

    无符号数:数值。

    表:xxx_info。

    Class文件格式:

    u4 u2 u2 u2 cp_info u2 u2 u2 u2 u2 u2 field_info u2 method_info u2 attribute_info

    解释:

    1、魔数:

    每个Class文件的头四个字节数为魔数,唯一作用是确定这个文件是否是能被虚拟机接受的Class文件

    目前版本的值都是固定的,0xCAFEBABE(咖啡宝贝)。

    2、紧接着魔数后面的4个字节就是Class文件的版本号:5-6字节,次版本号;7-8字节主版本号。

    3、Class文件版本号后面紧随的是常量池入口。

    常量池主要分为两大类:

    • 字面量:与Java中的常量概念类似;int a = 3,字面量就是等号右边的东西
    • 符号引用:符号引用包含三类常量。
      • 类和接口的全限名称:org.springframework.xxxBean。
      • 字段名称和描述符:private/protected/public。
      • 方法的名称和描述符:private/protected/public。

    常量池:

    常量池结构表:

    访问标志:

    分析示例

    public class Test extends Thread implements Serializable {
    
        public int count(int sum) {
            int i = sum = 1;
            return i;
        }
    }

    以上代码生成class文件用二进制格式打开后如下(部分):

    前置说明,address 00000000记为00,00000010记为10

    1、[00, 0]至[00, 7]如上所述为魔数和版本号,[00, 8]至[00, 9]为常量池计数器,换算下来为22,但因为常量池数量为常量池计数器-1,所以常量池的数量是21。

    2、然后我们来看看常量池的数据:

    从[00, a]开始,首先0a的10进制为10,对照上表是CONSTANT_Methodref_info

    我们将其列为:

    tag   = 10
    index = 00 03 >>> 00 03
    index = 13 07 >>> 19 07

    以此类推,[00, f]:class_info

    tag   = 07
    index = 00 14 >>> 00 20

    [00, f]:class_info

    tag   = 07
    index = 00 15 >>> 00 21

    3、但其实我们不用一步步的分析,可以直接通过javap -v Test.class命令就可以了。

    根据构造函数中invokespecial #1的入口分析后得出如下:

    第一步:#1,然后我们找到常量池的#1,其中会先到#3再到#19。

    第二步:#3,转到#21。

    第三步:#19,转到#5,#6,以此类推。

    其中我们可以看到<init>,这是对象实例的方法;如果是<clinit>则是类和接口初始化,如静态代码块的加载。

    4、接下来就是访问标志、类索引、父类索引、接口计数器等等,它们都对应两个字节。

    • 访问标志:[110, 5],[110, 6]两个字节,也就是00 21。因访问标志为public所以对应标志为0x0001,计算公式 >>> 访问标志1 | 访问标志2 | 访问标志n | ACC_SUPER的值。
      • public class >>> 0x0001 | 0x0020 >>> 0x0021,所以对应了00 21。
      • public abstract class >>> 0x0001 | 0x0400 | 0x0020 >>> 0x0421,你再重新编译便会发现值为04 21。
      • 以此类推,只要对照访问标志的字典表就好了。
    • 类索引:[110, 7],[110, 8] >>> 00 02,对应常量表#2,com/jdr/maven/rabbitmq/helloworld/Test。
    • 父类索引:[110, 9],[110, a] >>> 00 03,对应常量表#3,java/lang/Thread。
    • 接口计数器:[110, b],[110, c] >>> 00 01,也就是一个接口。
    • 内容太多就不一一分析了,只需按图索骥便可。
  • 相关阅读:
    2011年 CIO简历该怎么写?
    OC内存管理
    【Android游戏开发十五】关于Android 游戏开发中 OnTouchEvent() 触屏事件的性能优化笔记!
    【Android游戏开发十二】(保存游戏数据 [上文])详解SharedPreference 与 FIleInputStream/FileOutputStream将数据存储到SD卡中!
    ORA16014: 日志 1 的序列号 83 未归档, 没有可用的目的
    【Android游戏开发十四】深入Animation,在SurfaceView中照样使用Android—Tween Animation!
    2011来临 IT人员应该具备哪些技能?
    垃圾控件DatePicker
    【Android游戏开发十八】解放手指,利用传感器开发游戏!(本文讲解在SurfaceView中用重力传感器控制圆球的各方向移动)
    【Android游戏开发十三】(保存游戏数据 [下文])详解SQLite存储方式,并把SQLite的数据库文件存储在SD卡中!!!
  • 原文地址:https://www.cnblogs.com/bzfsdr/p/12097626.html
Copyright © 2011-2022 走看看