分析一个Java Class文件

标签: 分析 java class | 发表时间:2015-05-01 17:34 | 作者:vonzhoufz
出处:http://blog.csdn.net


Java源码文件TestClass.java:
package jvm.chapter6;
//P166
public class TestClass {
	private int m;
	public int inc(){
		return m+1;
	}
}


展示这个Class文件的16进制内容:




从头开始分析=>
ca fe ba be :magic number;
00 00 00 34 : 版本号是1.8.0;
00 16: 说明常量池有21个常量,1-21, index留做他用;接下来就是分别这21个常量的描述:
07/00 02 :CONSTANT_Class_info 常量,类名索引是该常量池的第2项;
01/00 16/6a 76 6d 2f 63 68 61 70  74 65 72 36 2f 54 65 73 74 43 6c 61 73 73 :这是该常量池的第2项,CONSTANT_Utf8_info常量,长度是22(0x0016),正是字符串常量“jvm/chapter6/TestClass”;
07/00  04:CONSTANT_Class_info 常量,类名索引是该常量池的第4项;
01/00 10/6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74:这是该常量池的第4项,CONSTANT_Utf8_info常量,长度是16(0x0010),正是字符串常量“java/lang/Object”;
01/00 01/6d :CONSTANT_Utf8_info常量,是字符串常量"m";
01/00 01/49 :CONSTANT_Utf8_info常量,是字符串常量"m";
01/00 06/3c  69 6e 69 74 3e :CONSTANT_Utf8_info常量,是字符串常量"<init>";
01/00 03/28 29 56 :CONSTANT_Utf8_info常量,是字符串常量"()V";
01/00 04/43 6f  64 65 :CONSTANT_Utf8_info常量,是字符串常量"Code";
0a/00 03/00 0b: tag=10,表示CONSTANT_Methodref_info常量,声明该方法的类名称位于常量池的第3项,即java/lang/Object,方法名和类型NameAndType="<init>":()V;
0c/00 07/00 08: 接下来tag=12就是上面刚说道的CONSTANT_NameAndType_info类型,接下来的两个索引分别指向方法名和描述符即"<init>"和“()V”,
01/00 0f/4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 :CONSTANT_Utf8_info常量,是字符串常量"LineNumberTable";
01/00 12/4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61  62 6c 65 :CONSTANT_Utf8_info常量,是字符串常量"LocalVariableTable";
01/00 04/74 68  69 73 :CONSTANT_Utf8_info常量,是字符串常量"this";
01/00 18/4c 6a 76 6d 2f 63 68 61 70 74 65 72 36 2f 54 65 73 74 43 6c 61 73 73 3b:CONSTANT_Utf8_info常量,是字符串常量"Ljvm/chapter6/TestClass;";
01/00 03 69 6e 63 :CONSTANT_Utf8_info常量,是字符串常量"inc";
01/00 03 28 29 49 :CONSTANT_Utf8_info常量,是字符串常量"()I";
09/00 01/00 13:代表Fieldref常量,然后接下来两个u2分别表示声明该字段的类和NameAndType的索引;
0c/00 05/00 06:就是上面刚使用的NameAndType常量;
01/00 0a/53 6f 75 72 63 65 46 69 6c 65 :CONSTANT_Utf8_info常量,是字符串常量"SourceFile";
01 00 0e 54 65 73 74 43  6c 61 73 73 2e 6a 61 76 61::CONSTANT_Utf8_info常量,是字符串常量"TestClass.java";


上面就分析完了常量池中的字段,
00 21 : access_flags= ACC_PUBLIC | ACC_SUPER = 0x0021;
00 01 00 03 00  00:类索引 父类索引 和接口索引,常量池第3项表示的是Object,说明了默认的父类就是Object,没有实现接口,所以interfaces_count=0;
00 01/00 02/00 05/00 06/00 00: 接下来的是字段表集合(fields_info),fields_count=1,access_flags=0x0002=ACC_PRIVATE, 代表字段名称的name_index=5(常量池的第5项是CONSTANT_Utf8_info类型的字符串m),字段描述符descriptor_index=0x0006(常量池的第5项是CONSTANT_Utf8_info类型的字符串I),最后atributes_count=0,说明不包含额外的信息。总上这些信息正是为了说明定义的字段“private int m”;


00 02/00 01/00  07/00 08/00 01/00 09/00 00 00 2f/00 01/00 01/00  00 00 05/2a b7 00 0a b1:接下来的是方法集合(methods_info),有两个方法,第一个方法的访问标志是0x0001=ACC_PUBLIC,名称索引是0x0007,指向常量池中的<init>,描述符索引值是0x0008,指向常量池中的"()V",接下来属性表计数器attributes_count=1,属性名称索引是0x0009对应的是“Code”,接下来就是Code属性表的结构:属性值的长度attribute_length=0x2f;操作数栈的最大深度和本地变量表的容量都是0x0001;字节码指令长度是5,翻译“2a b7 00 0a b1”的过程是:
1)读入2a,对应的是aload_0 ,将第一个引用类型的本地变量推送到操作数栈顶;
2)读入b7,对应的是invokespecial,作用是调用以栈顶的reference类型所指向的对象的构造器方法 实例初始化方法 私有方法,接下来有一个u2类型的参数说明具体调用哪个方法,它指向常量池中一个CONSTANT_Methodref_info的常量,即那个方法的符号引用;
3)00 0a正是上面invokespecial的参数,指向常量池中的一个Methodref常量,对应的应该是Object.init()方法;
4)b1,对应的指令是return,并且返回类型是oid,当前方法对应的指令结束。


继续上面的分析,
00 00/00 02/00 0c/00 00 00 06/00 01/00 00/00 03|00 0d/00 00 00 0c/00 01|00 00/00 05/00 0e/00 0f/00 00:exception_table_length=0,该方法没有抛出异常,然后attributes_count=2,该Code属性有俩属性:0x000c指向常量池中的“LineNumberTable”,attribute_length=6,line_number_table_length=1,就是记录了一对java源码行号和字节码行号的对应关系(3:0);0x000d指向常量池中的“LocalVariableTable”,attribute_length=12,local_variable_table_length=1(代表了一个栈帧和程序中的局部变量关联关系),start_pc=0x0000,length=0x0005表征该局部变量在字节码中的作用范围(就是这个方法内),name_index=0x000e, descriptor_index=0x000f表示该局部变量的名称及其描述符,即为“this”, "Ljvm/chapter6/TestClass";通过javap的输出都可以一目了然。index=0x0000代表该局部变量在栈帧局部变量表中Slot的位置。


接下来看第二个方法,
00 01/00 10/00 11/00 01/00 09/00 00 00 31/00 02/00 01/00 00 00 07/2a b4 00 12 04 60 ac/:方法的访问标志是0x0001=ACC_PUBLIC,名称索引是16,指向常量池中的“inc”,描述符索引值是0x0011,指向常量池中的"()I",接下来属性表计数器attributes_count=1,属性名称索引是0x0009对应的是“Code”,接下来就是Code属性表的结构:属性值的长度attribute_length=0x31;操作数栈的最大深度是2,本地变量表的容量是1;字节码指令长度是7,翻译“2a b4 00 12 04 60 ac”的过程是:
1)读入2a,对应的是aload_0 ,将第一个引用类型的本地变量推送到操作数栈顶;
2)读入b4,对应的指令是getfield,获得实例域,并压入栈顶,他需要参数用来说明把哪个变量入栈,即下面;
3)读入00 12,用来指明getfield 哪个字段,指向常量池中的一个Fieldref常量,“jvm/chapter6/TestClass.m:I”;
4)读入04,对应的指令是iconst_1,将int类型的1推至栈顶;
5)读入60,对应的指令是iadd,将栈顶两个int类型的数值相加并把结果压入栈顶;
6)读入ac,对应的指令是ireturn,从当前方法返回int,结束。
后面的俩属性就和分析第一个方法类似。


至此,就分析完了一个简单的Class文件的结构,清晰了很多。


附:

Javap TestClass.class的结果:$ javap -verbose TestClass.class 

Classfile /home/vonzhou/GitHub/JavaProject/learning-java/bin/jvm/chapter6/TestClass.class
  Last modified Apr 29, 2015; size 379 bytes
  MD5 checksum 70a67e773b621619d03f9d1b5ac91af6
  Compiled from "TestClass.java"
public class jvm.chapter6.TestClass
  SourceFile: "TestClass.java"
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2;            //  jvm/chapter6/TestClass
   #2 = Utf8               jvm/chapter6/TestClass;
   #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               Ljvm/chapter6/TestClass;;
  #16 = Utf8               inc;
  #17 = Utf8               ()I;
  #18 = Fieldref           #1.#19;        //  jvm/chapter6/TestClass.m:I
  #19 = NameAndType        #5:#6;         //  m:I
  #20 = Utf8               SourceFile;
  #21 = Utf8               TestClass.java;
{
  public jvm.chapter6.TestClass();
    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   Ljvm/chapter6/TestClass;


  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   Ljvm/chapter6/TestClass;
}



作者:vonzhoufz 发表于2015/5/1 9:34:11 原文链接
阅读:66 评论:1 查看评论

相关 [分析 java class] 推荐:

分析一个Java Class文件

- - CSDN博客推荐文章
Java源码文件TestClass.java:. 展示这个Class文件的16进制内容:. 00 00 00 34 : 版本号是1.8.0;. 00 16: 说明常量池有21个常量,1-21, index留做他用;接下来就是分别这21个常量的描述:. 07/00 02 :CONSTANT_Class_info 常量,类名索引是该常量池的第2项;.

Java的 class文件结构

- - Java - 编程语言 - ITeye博客
Java-class文件结构.        我们都知道我们现在写的源代码计算机是不认识的,我们需要根据指定的编译器进行编译-连接-执行,这样才是我们想要的结果,所以计算机只能认识0或者1 ,那么如何与操作系统或者机器指令无关的程序能执行,那么在操作系统以及机器指令之上的那就是虚拟机了,这样我们编写的代码不再是最终形成二进制本地指令代码,而是一种在操作系统和机器指令之上的虚拟机规定的文件格式.

Java编程的动态特性, 从Reflection到Runtime Class Transformation

- - 编程语言 - ITeye博客
关于Java编程的动态特性,从认识的过程上要从 Reflection 到 instrumentation. 初步的开发者刚接触到Reflection会非常兴奋,因为反射可以在运行时完成很多之前不可能的任务,这件利器使人打破了很多束缚. Java Annotation出现后,更让Java变得更加有活力,更加友好.

java读取指定package下的所有class

- - 互联网 - ITeye博客
之前在看spring注解的时候,有看到再配置文件里面定义component scan package就能自动扫描对应包下面的class,然后根据注解生成相应的bean. 自己对这个功能很好奇,就搜了下,找到了实现的关键代码,记录下. * 从包package中获取所有的Class. // 第一个class类的集合.

[Java] Java 多线程案例分析

- - V2EX
现要从 hbase中导出 2016 年整年的,大约 10w只股票行情数据,数据总量约 100t. 汇总到 hdfs中供需求方使用. 已知数据量规模大概是 100t,那么单台机器处理肯定不是不行的,先不说大多数磁盘都没这么大,即便磁盘有这么大,单台机器处理对于内存和 cpu 要求也很高,所以我们将问题一般化,使用数量有限的低配机器.

Django class-based view 基础

- Ken - python.cn(jobs, news)
自从Django在1.3中新增了class-based view以来,还没有仔细研究它,开始感觉这个东西是否有点多余. 因为Django已经有了Generic veiws了啊, 可是仔细看过class-based veiw之后, 这种想法打消了, 因为你完全可以用类方法实现你所有的视图, 而代码阅读起来却更容易!.

Django class-based view 深入

- Ken - python.cn(jobs, news)
上一篇我们粗略介绍了Django中的class-based view基础知识, 本篇我们继续来看关于class-based view的高级应用.. 我们继续沿用上篇中的model:. 我们来看看如何对一个Book实例进行更新, 我们要做的只是在视图类中更新 :.     template_name = 'updatebook.html'  #这里是你的模板文件名.

标签?ID?还是CLASS?

- - 前端观察
想谈一下几个基本的HTML问题,都是围绕着应该怎样使用HTML. 多用有语义的标签,少用div和span,避免使用没有class的div和span. 设想一下HTML的世界最初只有div和span这两个标签,其实网页依然可以写得出来. 更多标签的出现,其实是为了替代利用率高但不好书写的 
 和  来的.

java线程池分析

- - BlogJava-首页技术区
    在Java 5.0之前启动一个任务是通过调用Thread类的start()方法来实现的,任务的提于交和执行是同时进行的,如果你想对任务的执行进行调度或是控制 同时执行的线程数量就需要额外编写代码来完成. 5.0里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行,并且可以建立一个类似数据库连接 池的线程池来执行任务.

JAVA集合分析概述

- - ITeye博客
       前段时间在QQ空间写了篇关于JAVA集合分析的日志,现搬到这里与大家分享. 其中有链接可查看各种集合的具体介绍.  最基本的数据结构有数组、链表2种.         1)数组的长度是固定的,容量是有限的(除非扩容). 可以用索引访问,因此访问速度比较快.         2)链表中每个节点指向下一个节点,容量是无限的(不作限制且不考虑硬件).