Java class文件格式之属性详解_动力节点java学院整理

Code属性

code属性是方法的一个最重要的属性。 因为它里面存放的是方法的字节码指令, 除此之外还存放了和操作数栈,局部变量相关的信息。 所有不是抽象的方法, 都必须在method_info中的attributes中有一个Code属性。下面是Code属性的结构, 为了更直观的展示Code属性和method_info的包含关系, 特意画出了method_info:

下面依次介绍code属性中的各个部分。

attribute_name_index指向常量池中的一个CONSTANT_Utf8_info , 这个CONSTANT_Utf8_info 中存放的是当前属性的名字 “Code” 。

attribute_length给出了当前Code属性的长度(不包括前六字节)。

max_stack 指定当前方法被执行引擎执行的时候, 在栈帧中需要分配的操作数栈的大小。

max_locals指定当前方法被执行引擎执行的时候, 在栈帧中需要分配的局部表量表的大小。注意, 这个数字并不是局部变量的个数, 因为根据局部变量的作用域不同, 在执行到一个局部变量以外时, 下一个局部变量可以重用上一个局部变量的空间(每个局部变量在局部变量表中占用一个或两个Slot)。 方法中的局部变量包括方法的参数, 方法的默认参数this, 方法体中定义的变量, catch语句中的异常对象。 关于执行引擎的相关内容会在后面的博客中讲到。

code_length指定该方法的字节码的长度, class文件中每条字节码占一个字节。

code存放字节码指令本身, 它的长度是code_length个字节。

exception_table_length 指定异常表的大小

exception_table就是所谓的异常表, 它是对方法体中try-catch_finally的描述。 exception_table可以看做是一个数组, 每个数组项是一个exception_info结构, 一般来说每个catch块对应一个exception_info,编译器也可能会为当前方法生成一些exception_info。 exception_info的结构如下(为了直观的显示exception_info, exception_table和Code属性的关系, 画出了Code属性,的话读者就会更清楚各个数据项之间的位置关系和包含关系):

下面讲解exception_info中的各个字段的意思。

start_pc是从字节码(Code属性中的code部分)起始处到当前异常处理器起始处的偏移量。

end_pc是从字节码起始处到当前异常处理器末尾的偏移量。

handler_pc是指当前异常处理器用来处理异常(即catch块)的第一条指令相对于字节码开始处的偏移量。

catch_type是一个常量池索引, 指向常量池中的一个CONSTANT_Class_info数据项, 该数据项描述了catch块中的异常的类型信息。这个类型必须是java.lang.Throwable的或其子类。

所以可以总结, 一个异常处理器(exception_info)的意思是: 如果偏移量从start_pc到end_pc之间的字节码出现了catch_type描述的类型的异常, 那么就跳转到偏移量为handler_pc的字节码处去执行。如果catch_type为0, 就代表不引用任何常量池项(再回顾一下, 常量池中的项是从1开始计的), 那么这个exception_info用于实现finally子句。

我们一直在介绍Code属性, 只不过刚才进行了一个小插曲, 介绍了Code属性中的exception_table中的exception_info的详细信息。 下面我们继续介绍Code 属性中的其他信息, 希望读者不要被绕晕了 : )

attributes_count 表示当前Code 属性中存在的其他属性的个数。 现在我们知道, class中的属性, 不仅会出现在顶层的class中, 会存在field_info中, 会存在method_info中, 甚至还会出现在属性中。

attributes可以看做是一个数组, 里面存放了Code属性中的其他属性。 Code 属性中可以出现的其他属性有LineNumberTable和LocalVariableTable 。 这两个属性会在下面介绍。

LineNumberTable属性

LineNumberTable属性存在于Code属性中, 它建立了字节码偏移量到源代码行号之间的联系。 这个属性是可选的, 编译器可以选择不生成该属性。下面是该属性的结构(同样给出了全局的位置关系,LineNumberTable在图的右下角部分):

由于这个属性并不是重点, 我们在此简单的讲述。

每个LineNumberTable中的line_number_table部分, 可以看做是一个数组, 数组的每项是一个line_number_info , 每个line_number_info 结构描述了一条字节码和源码行号的对应关系。 其中start_pc是这个line_number_info 描述的字节码指令的偏移量, line_number是这个line_number_info 描述的字节码指令对应的源码中的行号。可以看出, 方法中的每条字节码都对应一个line_number_info , 这些line_number_info 中的line_number可以指向相同的行号, 因为一行源码可以编译出多条字节码。

LocalVariableTable属性 

LocalVariableTable 属性建立了方法中的局部变量与源代码中的局部变量之间的对应关系。 这个属性存在于Code属性中。 这个属性是可选的, 编译器可以选择不生成这个属性。该属性的结构如下:(同样给出了全局的位置关系图,LocalVariableTable 在该图的右下角 )

由于这个属性相对不那么重要, 这里只是大概讲解一下。

每个LocalVariableTable 的local_variable_table部分可以看做是一个数组, 每个数组项是一个叫做local_variable_info的结构, 该结构描述了某个局部变量的变量名和描述符, 还有和源代码的对应关系。下面讲解local_variable_info的各个部分:

start_pc是当前local_variable_info所对应的局部变量的作用域的起始字节码偏移量;

length是当前local_variable_info所对应的局部变量的作用域的大小。 也就是从字节码偏移量start_pc 到start_pc+length就是当前局部变量的作用域范围;

name_index指向常量池中的一个CONSTANT_Utf8_info, 该CONSTANT_Utf8_info描述了当前局部变量的变量名;

descriptor_index指向常量池中的一个CONSTANT_Utf8_info, 该CONSTANT_Utf8_info描述了当前局部变量的描述符;

index描述了在该方法被执行时,当前局部变量在栈帧中局部变量表中的位置。

由此可知, 方法中的每个局部变量都会对应一个local_variable_info 。

Exceptions属性

首先需要说明, Exceptions属性不是存在于Code属性中的, 它存在于method_info中的attributes中。 和Code属性是平级的。 这个属性描述的是方法声明的可能会抛出的异常, 也就是方法定义后面的throws声明的异常列表, 请不要和上面提到的异常处理器混淆。 异常处理器描述了方法的字节码如何处理异常, 而Exceptions属性描述方法可能会抛出哪些以异常。下面讲解Exceptions属性的结构(左下角为Exceptions属性):

下面讲解Exceptions属性中的信息。

attribute_name_index和attribute_length就不多说了, 和其他属性是一样的。

number_of_exceptions是该方法要抛出的异常的个数。

exceptions_index_table可以看做一个数组, 这个数组中的每一项占两个字节, 这两个字节是对常量池的索引, 它指向一个常量池中的CONSTANT_Class_info。 这个CONSTANT_Class_info描述了一个被抛出的异常的类型。

总结

到此为止, 和方法相关的属性就介绍完了。 这篇博客讲解的内容相对比较复杂。 下面以一个实例进行验证, 实例代码:

package com.bjpowernode.test;
public class Test {
  public void test() throws Exception{
    int localVar = 0;
    try{
      Class.forName("com.bjpowernode.test.Person");
    }catch(ClassNotFoundException e){
      throw e;
    }finally{
      System.out.println(localVar);
    }
  }
}

反编译后的test方法部分(省略了常量池等信息):

public void test() throws java.lang.Exception;
 flags: ACC_PUBLIC
 Exceptions:
  throws java.lang.Exception
 Code:
  stack=2, locals=4, args_size=1
    0: iconst_0
    1: istore_1
    2: ldc      #18         // String com.bjpowernode.test.Person
    4: invokestatic #20         // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
    7: pop
    8: goto     24
   11: astore_2
   12: aload_2
   13: athrow
   14: astore_3
   15: getstatic   #26         // Field java/lang/System.out:Ljava/io/PrintStream;
   18: iload_1
   19: invokevirtual #32         // Method java/io/PrintStream.println:(I)V
   22: aload_3
   23: athrow
   24: getstatic   #26         // Field java/lang/System.out:Ljava/io/PrintStream;
   27: iload_1
   28: invokevirtual #32         // Method java/io/PrintStream.println:(I)V
   31: return
  Exception table:
    from  to target type
      2   8  11  Class java/lang/ClassNotFoundException
      2  14  14  any
  LineNumberTable:
   line 7: 0
   line 11: 2
   line 13: 8
   line 15: 12
   line 16: 14
   line 17: 15
   line 18: 22
   line 17: 24
   line 20: 31
  LocalVariableTable:
   Start Length Slot Name  Signature
       0   32   0 this  Lcom/bjpowernode/test/Test;
       2   30   1 localVar  I
      12    2   2   e  Ljava/lang/ClassNotFoundException; 

结合上面的讲解和图解, 再分析反编译的结果, 就一目了然了: 所有的结果是一个method_info, method_info开始处是访问标志信息。 然后是method_info的 Exceptions属性 , Exceptions属性属性下面是Code属性, Code属性中又包括字节码, 异常处理器 ,LineNumberTable属性和LocalVariableTable 属性。

所以会直接或间接的和method_info有联系, 最后给出一张全局图, 这样的话, 读者就比较明确, 一个完整的方法, 是如何在class文件中描述的,由于考虑到复杂性, 这些属性或其他数据项中, 对常量池的引用均未画出:

(0)

相关推荐

  • java.lang.NoClassDefFoundError错误解决办法

    java.lang.NoClassDefFoundError错误解决办法 前言 在日常Java开发中,我们经常碰到java.lang.NoClassDefFoundError这样的错误,需要花费很多时间去找错误的原因,具体是哪个类不见了?类明明还在,为什么找不到?而且我们很容易把java.lang.NoClassDefFoundError和java.lang.ClassNotfoundException这两个错误搞混,事实上这两个错误是完全不同的.我们往往花费时间去不断尝试一些其他的方法去解决这

  • Java中Class类的作用与深入理解

    Java中Class类的作用与深入理解 在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识.这个信息跟踪着每个对象所属的类.JVM利用运行时信息选择相应的方法执行.而保存这些信息的类称为Class.可能容易产生混淆,容易想到class.不过二者没什么关系,class不过是描述类的一个关键字.而Class却是保存着运行时信息的类. 它能做什么?Class类可以帮助我们在程序运行时分析类,说白了就是获取类中的值.可能瞬间就想到了反射,没错!Class一般就是和反射配套使

  • Java class文件格式之访问标志信息_动力节点Java学院整理

    class文件中的访问标志信息 位于常量池下面的2个字节是access_flags . access_flags 描述的是当前类(或者接口)的访问修饰符, 如public, private等, 此外, 这里面还存在一个标志位, 标志当前的额这个class描述的是类, 还是接口.access_flags 的信息比较简单, 下面列出access_flags 中的各个标志位的信息.本来写这个系列博客参考的是<深入java虚拟机>, 但是这本书比较老了, 关于java 5以后的新特性没有进行解释,这本

  • Java Class 解析器实现方法示例

    最近在写一个私人项目,名字叫做ClassAnalyzer,ClassAnalyzer的目的是能让我们对Java Class文件的设计与结构能够有一个深入的理解.主体框架与基本功能已经完成,还有一些细节功能日后再增加.实际上JDK已经提供了命令行工具javap来反编译Class文件,但本篇文章将阐明我实现解析器的思路. Class文件 作为类或者接口信息的载体,每个Class文件都完整的定义了一个类.为了使Java程序可以"编写一次,处处运行",Java虚拟机规范对Class文件进行了严

  • Java 使用getClass().getResourceAsStream()方法获取资源

    Java 使用getClass().getResourceAsStream()方法获取资源 之前想获取一个资源文件做一些处理,使用getClass().getResourceAsStream()一直拿不到文件. 具体的用法. 1 InputStream is = this.getClass().getResourceAsStream(fileName); //拿不到资源 2 InputStream is = this.getClass().getResourceAsStream("/"

  • java动态添加外部jar包到classpath的实例详解

    java动态添加外部jar包到classpath的实例详解 前言: 在项目开发过程中我们有时候需要动态的添加外部jar包,但是具体的业务需求还没有遇到过,因为如果动态添加外部jar包后,我们就需要修改业务代码,而修改代码就需要重新启动服务,那样好像就没有必要动态添加外部jar包了,怎么样才能不重新启动服务器就可以使用最新的代码我没有找到方法,如果各位知道的话给我点建议,回归主题,实现动态添加外部jar包到classpath的方法如下: String beanClassName = "com.dy

  • Java class文件格式之属性详解_动力节点java学院整理

    Code属性 code属性是方法的一个最重要的属性. 因为它里面存放的是方法的字节码指令, 除此之外还存放了和操作数栈,局部变量相关的信息. 所有不是抽象的方法, 都必须在method_info中的attributes中有一个Code属性.下面是Code属性的结构, 为了更直观的展示Code属性和method_info的包含关系, 特意画出了method_info: 下面依次介绍code属性中的各个部分. attribute_name_index指向常量池中的一个CONSTANT_Utf8_in

  • Java中的clone方法详解_动力节点Java学院整理

    Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有几种方式可以创建对象呢? 1 使用new操作符创建一个对象 2 使用clone方法复制一个对象 那么这两种方式有什么相同和不同呢? new操作符的本意是分配内存.程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间.分配完内存

  • Java BigDecimal详解_动力节点Java学院整理

    1.引言 借用<Effactive Java>这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的.然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合.但是,商业计算往往要求结果精确,例如银行存款数额,这时候BigDecimal就派上大用场啦. 2.BigDecimal简介 BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组

  • Java System类详解_动力节点Java学院整理

    System类是jdk提供的一个工具类,有final修饰,不可继承,由名字可以看出来,其中的操作多数和系统相关.其功能主要如下: • 标准输入输出,如out.in.err • 外部定义的属性和环境变量的访问,如getenv()/setenv()和getProperties()/setProperties() • 加载文件和类库的方法,如load()和loadLibrary(). • 一个快速拷贝数组的方法:arraycopy() • 一些jvm操作,如gc().runFinalization()

  • Java Runtime类详解_动力节点Java学院整理

    一.概述 Runtime类封装了运行时的环境.每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接.一般不能实例化一个Runtime对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用.一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为. 当不被信任的代码调用任何Runtime方法时,常常会引起SecurityExc

  • Java Scaner类详解_动力节点Java学院整理

    Java.util.Scanner是Java5.0的新特征,主要功能是简化文本扫描.这个类最实用的地方表现在获取控制台输入,其他的功能都很鸡肋,尽管Java API文档中列举了大量的API方法,但是都不怎么地. 一.扫描控制台输入  这个例子是常常会用到,但是如果没有Scanner,你写写就知道多难受了. 当通过new Scanner(System.in)创建一个Scanner,控制台会一直等待输入,直到敲回车键结束,把所输入的内容传给Scanner,作为扫描对象.如果要获取输入的内容,则只需要

  • HTTP协议详解_动力节点Java学院整理

    一.概念 协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则,超文本传输协议(HTTP)是一种通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器. HTTP协议,即超文本传输协议(Hypertext transfer protocol).是一种详细规定了浏览器和万维网(WWW = World Wide Web)服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议. HTTP协议是用于从WWW服务器传输超文本到本地浏览器的传送协议.

  • web.xml详解_动力节点Java学院整理

    一.            Web.xml详解: (一)  web.xml加载过程(步骤) 首先简单说一下,web.xml的加载过程. 当我们去启动一个WEB项目时,容器包括(JBoss.Tomcat等)首先会读取项目web.xml配置文件里的配置,当这一步骤没有出错并且完成之后,项目才能正常地被启动起来.   启动WEB项目的时候,容器首先会去它的配置文件web.xml读取两个节点: <listener></listener>和<context-param><

  • Apache和Nginx的优缺点详解_动力节点Java学院整理

    Apache和Nginx比较 功能对比 Nginx和Apache一样,都是HTTP服务器软件,在功能实现上都采用模块化结构设计,都支持通用的语言接口,如PHP.Perl.Python等,同时还支持正向和反向代理.虚拟主机.URL重写.压缩传输.SSL加密传输等. 在功能实现上,Apache的所有模块都支持动.静态编译,而Nginx模块都是静态编译的, 对FastCGI的支持,Apache对Fcgi的支持不好,而Nginx对Fcgi的支持非常好: 在处理连接方式上,Nginx支持epoll,而Ap

  • Java Object类详解_动力节点Java学院整理

    Java作为一个庞大的知识体系,涉及到的知识点繁多,本文将从Java中最基本的类java.lang.Object开始谈起. Object类是Java中其他所有类的祖先类,没有Object类Java面向对象无从谈起.作为其他所有类的基类,Object具有哪些属性和行为,是Java语言设计背后的思维体现. Object类位于java.lang包中,java.lang包包含着Java最基础和核心的类,在编译时会自动导入.Object类没有定义属性,一共有13个方法,具体的类定义结构如下图: 1.类构造

随机推荐