• java class文件结构


    看了一周深入理解java虚拟机了,今天看到第六章,最近一直处在接受新知识的快感之中,不由得感慨基础知识的重要性,学起来相当过瘾!类文件结构这部分实践性较强,对于分析java代码有很重要的帮组,于是有做点笔记的必要了,在参考书的基础上,将一段代码的字节码完全解析了一遍。
    class文件的结构如下

    ClassFile {
    	u4 magic;
    	u2 minor_version;
    	u2 major_version;
    	u2 constant_pool_count;
    	cp_info constant_pool[constant_pool_count-1];
    	u2 access_flags;
    	u2 this_class;
    	u2 super_class;
    	u2 interfaces_count;
    	u2 interfaces[interfaces_count];
    	u2 fields_count;
    	field_info fields[fields_count];
    	u2 methods_count;
    	method_info methods[methods_count];
    	u2 attributes_count;
    	attribute_info attributes[attributes_count];
    }
    编译下面这段代码生成class文件
    package org.fenixsoft.clazz;
    public class Test {
    	private int m;
    	public int inc(){
    		return m+1;
    	}
    }

    对应class文件内容如下

    cafe babe 0000 0033 0016 0700 0201 0018
    6f72 672f 6665 6e69 7873 6f66 742f 636c
    617a 7a2f 5465 7374 0700 0401 0010 6a61
    7661 2f6c 616e 672f 4f62 6a65 6374 0100
    016d 0100 0149 0100 063c 696e 6974 3e01
    0003 2829 5601 0004 436f 6465 0a00 0300
    0b0c 0007 0008 0100 0f4c 696e 654e 756d
    6265 7254 6162 6c65 0100 124c 6f63 616c
    5661 7269 6162 6c65 5461 626c 6501 0004
    7468 6973 0100 1a4c 6f72 672f 6665 6e69
    7873 6f66 742f 636c 617a 7a2f 5465 7374
    3b01 0003 696e 6301 0003 2829 4909 0001
    0013 0c00 0500 0601 000a 536f 7572 6365
    4669 6c65 0100 0954 6573 742e 6a61 7661
    0021 0001 0003 0000 0001 0002 0005 0006
    0000 0002 0001 0007 0008 0001 0009 0000
    002f 0001 0001 0000 0005 2ab7 000a b100
    0000 0200 0c00 0000 0600 0100 0000 0300
    0d00 0000 0c00 0100 0000 0500 0e00 0f00
    0000 0100 1000 1100 0100 0900 0000 3100
    0200 0100 0000 072a b400 1204 60ac 0000
    0002 000c 0000 0006 0001 0000 0006 000d
    0000 000c 0001 0000 0007 000e 000f 0000
    0001 0014 0000 0002 0015 

    解析:

    /*魔数*/
    cafe babe // 魔数
    
    
    /*次版本*/
    0000 // 次版本
    
    
    /*主版本*/
    0033 // 主版本
    
    
    /*常量池个数*/
    0016 // 常量个数,21(从1开始)
    
    
    /*cp_info类型的数组*/
    //21个常量
    0700 0201 0018
    6f72 672f 6665 6e69 7873 6f66 742f 636c
    617a 7a2f 5465 7374 0700 0401 0010 6a61
    7661 2f6c 616e 672f 4f62 6a65 6374 0100
    016d 0100 0149 0100 063c 696e 6974 3e01
    0003 2829 5601 0004 436f 6465 0a00 0300
    0b0c 0007 0008 0100 0f4c 696e 654e 756d
    6265 7254 6162 6c65 0100 124c 6f63 616c
    5661 7269 6162 6c65 5461 626c 6501 0004
    7468 6973 0100 1a4c 6f72 672f 6665 6e69
    7873 6f66 742f 636c 617a 7a2f 5465 7374
    3b01 0003 696e 6301 0003 2829 4909 0001
    0013 0c00 0500 0601 000a 536f 7572 6365
    4669 6c65 0100 0954 6573 742e 6a61 7661
    
    
    /*访问标志*/
    0021 // 访问标志(使用标志位)
    
    
    /*类索引*/
    0001 // 类索引
    
    
    /*父类索引*/
    0003 // 父类索引
    
    
    /*接口个数*/
    0000 // 接口索引:u2类型的接口计数器 + 各个接口
    /*u2类型的接口数组*/
    
    
    /*字段表数量*/
    0001 // 字段表数量
    /*field_info类型数组*/
    0002 // 字段访问标志
    0005 // 简单名称
    0006 //描述符
    0000 // 字段的属性表数量
    --- // 字段的属性表集合(0个)
    
    
    /*方法表数量*/
    0002 // 方法表数量
    
    
    /*method_info数组*/
    //第一个方法
    0001 // 方法访问标志
    0007 // 简单名称
    0008 // 描述符
    0001 // 属性表数量
    // 方法的属性表(对应的常量是Code,方法表中使用,代表代码编译成的字节码指令)
    0009 // Code属性表  u2类型,在常量池中
    0000 002f // u4类型,指明属性值所占位数
    0001 //max_stack,操作数栈最大深度,根据它分配栈帧
    0001 //max_locals,局部变量表所需的存储空间,单位是Slot,不超过32位的数据类型,占一个Slot,64位的占2个Slot
    0000 0005 // code length,
    // code,共5个
    2a // aload_0(将第0个Slot中为refrence类型的本地变量推送至操作数栈顶,其实就是this)
    b7 // invokespecial(调用栈顶对象的某个方法:实例构造方法、private方法或者它的父类方法)
    000a // invokespecial的参数,说明具体调用哪个方法,指向常量池中CONSTANT_Methodref_info类型常量
    b1 // 返回return,返回值为void
    0000 // 异常表长度
    //异常信息,长度为0
    ----
    0002 // code的属性表数量
    // 方法的属性表,在Code属性表中
    000c // 表示LineNumberTable,java源码与字节码的偏移量之间的关系
    0000 0006 // 属性表长度
    0001 // 行号表的长度
    // 行号信息
    0000 // start_pc
    0003 // line_number
    // 方法的属性表,在Code属性表中
    000d // 表示LocalVariableTable,描述栈帧中局部变量表中的变量与java源码中定义的变量之间的关系
    0000 000c // 该属性表长度
    0001 // 局部变量表长度
    // 局部变量信息,长度为1,由5个u2类型构成
    0000 // start_pc,该局部变量的生命周期开始的字节码偏移量
    0005 // length,其作用范围覆盖的长度
    000e // name_index,指向常量池中的索引,局部变量的名称
    000f // descriptor_index,同上,局部变量的描述符
    0000 // index,该局部变量在栈帧局部变量表中的Slot位置
    // 第二个方法
    0001 // 访问标志
    0010 // 简单名称,inc
    0011 // 描述符()I
    0001 // 属性表数量
    0009 // // Code属性表
    0000 0031 // u4类型,指明属性值所占位数
    0002 // max_stack
    0001 // max_locals
    0000 0007 // code_length
    2a // aload_0(将第0个Slot中为refrence类型的本地变量推送至操作数栈顶,其实就是this)
    b4 // getfield,获取指定类的实例,并将其压入栈帧
    0012 // 参数,Test类的m成员
    04 // iconst_1,将int型1推送至栈顶
    60 // iadd,将栈顶两个int型数值相加并将结果压入栈顶
    ac // ireturn,从当前方法返回int
    0000 // 异常表长度
    ---- //异常信息,长度为0
    0002 // 属性表数量
    // LineNumberTable
    000c 0000 0006 0001 0000 0006
    // LocalVariableTable
    000d 0000 000c 0001 0000 0007 000e 000f 0000
    
    
    /*类的属性表个数*/
    0001 // 属性表数量
    
    
    /*类的attribute_info数组*/
    0014 // SourceFile,记录原文件名称
    0000 0002 // 属性表长度
    0015 // 源文件,Test.java

    属性表可以包含在Class文件、字段表、方法表中。

    短短几行代码,我们看起来如此费劲,使用Oracle公司给我们准备的class文件字节码的工具javap。javap -v Test.java命令查看的字节码的内容如下,两者内容是一模一样的

    Last modified 2015-12-11; size 378 bytes
      MD5 checksum 0e2626e6fdb11bccf73564affa0b63e2
      Compiled from "Test.java"
    public class org.fenixsoft.clazz.Test
      SourceFile: "Test.java"
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Class              #2             //  org/fenixsoft/clazz/Test
       #2 = Utf8               org/fenixsoft/clazz/Test
       #3 = Class              #4             //  java/lang/Object
       #4 = Utf8               java/lang/Object
       #5 = Utf8               m
       #6 = Utf8               I
       #7 = Utf8               <init>
       #8 = Utf8               ()V
       #9 = Utf8               Code
      #10 = Methodref          #3.#11         //  java/lang/Object."<init>":()V
      #11 = NameAndType        #7:#8          //  "<init>":()V
      #12 = Utf8               LineNumberTable
      #13 = Utf8               LocalVariableTable
      #14 = Utf8               this
      #15 = Utf8               Lorg/fenixsoft/clazz/Test;
      #16 = Utf8               inc
      #17 = Utf8               ()I
      #18 = Fieldref           #1.#19         //  org/fenixsoft/clazz/Test.m:I
      #19 = NameAndType        #5:#6          //  m:I
      #20 = Utf8               SourceFile
      #21 = Utf8               Test.java
    {
      public org.fenixsoft.clazz.Test();
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #10                 // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0       5     0  this   Lorg/fenixsoft/clazz/Test;
    
      public int inc();
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0
             1: getfield      #18                 // Field m:I
             4: iconst_1
             5: iadd
             6: ireturn
          LineNumberTable:
            line 6: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0       7     0  this   Lorg/fenixsoft/clazz/Test;
    }

  • 相关阅读:
    学习 CosmosDB (NoSql)
    <linux-sed> sed基本用法
    grep 正则表达式用引号括起来和元字符加反斜杠转义的测试
    CACTI批量添加linux主机sh脚本
    一个简单的C共享库的创建及Python调用此库的方法
    Linux下的C的开发之GCC的初级使用
    AcitveReocrd事件和关联操作
    Samrty技术的 初步了解
    Ubuntu下配置Tomcat
    在ubuntu中安装jdk
  • 原文地址:https://www.cnblogs.com/qhyuan1992/p/5385273.html
Copyright © 2020-2023  润新知