热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于Java虚拟机二三事(七)类文件结构(下)

1.前言

    class文件存储格式中对方法的描述与对字段的描述几乎采用了完全一致的方式,方法表的结构如同字段表一样,依次包括了访问标注(access_flag)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attributes)几项。

2. 概述

    方法表集合是指由若干个方法表(method_info)组成的集合,对于在类中定义的若干个,经过JVM编译成class文件后,会将相应的method方法信息组织到一个叫做方法表集合的结构中,方法表集合是一个类数组结构,如下所示。

        关于Java虚拟机二三事(七)---类文件结构(下)                     3.method方法的描述——方法表集合在class文件中的位置

    method方法描述的方法表集合紧跟在字段表集合(字段表的介绍可参考 关于Java虚拟机二三事(六)---类文件结构(中))的后面,如下所示。

        关于Java虚拟机二三事(七)---类文件结构(下)

    这里可以看到一个新的表类型——method_info

4.method方法信息组成——method_info结构体定义

    对于一个方法的表示,可以由如下部分组成。

        关于Java虚拟机二三事(七)---类文件结构(下)

    实际上JVM还会对method方法的描述添加其他信息,如上图中的method_info结构体的定义,该结构体定义跟描述field字段field_info结构体几乎完全一致。如下图所示。

        关于Java虚拟机二三事(七)---类文件结构(下)

    方法表的结构如同字段表一样,依次包括了访问标注(access_flag)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attributes)几项。

    访问标志(access_flag)

   method_info结构体最前面的两个字节表示访问标志(access_info),记录这个方法的作用域、静态or非静态、可变性、是否可同步、是否为本地方法、是否为抽象等信息,实际上不止这些信息,后文会对其做详细介绍。

    名称索引(name_index)

    紧跟在访问标志(access_flag)后面的两个字节名称索引(name_index),这两个字节的值指向了常量池中的某一项常量项,这个方法的名称以UTF-8格式的字符串存储在这个常量池项中,如public void methodName,很显然,“methodName”表示这个方法的名称,那么常量池中会有一个CONSTANT_UTF8_info格式的常量池项,里面存储“methodName”字符串,而methodName()方法的方法表中的名称索引则指向这个常量池项。

    描述索引(descriptor_index)

    描述索引表示的是这个方法的特征或者说是签名,一个方法会有若干个参数返回值,而若干个参数的数据类型和返回值的数据类型构成了这个方法描述,其基本格式为:(参数数据类型描述列表)返回值数据类型。后续将会继续讨论

    属性表集合(attribute_info)

    这个属性表集合非常重要,方法的实现被JVM编译成成JVM的机器码指令机器码指令就是存放在一个Code类型的属性表中,如果方法声明要抛出异常,那么异常信息会在一个Exceptions类型的属性表中予以展现。Code类型的属性表可以说是非常复杂的内容,是本文的重点和难点。

    接下来,将对上文提及的四个概念进行逐一讲解。

4.1 访问标志(access_flag)---记录method方法的访问信息。

    访问标志(access_flags)共占有2 个字节,分为 16 位,这 16位 表示的含义如下所示:

    关于Java虚拟机二三事(七)---类文件结构(下)

举例:某个类中定义了如下方法:

[java] view plain copy
  1. public static synchronized final void greeting(){  
  2. }  

    greeting()方法的修饰符有:public、static、synchronized、final这几个修饰符修饰,那么相对应地,greeting()方法的访问标志中的ACC_PUBLIC、ACC_STATIC、ACC_SYNCHRONIZED、ACC_FINAL标志位都应该是1,即:

        关于Java虚拟机二三事(七)---类文件结构(下)

    从上图中可以看出访问标志的值应该是二进制00000000 00111001,即十六进制0x0039。我们将在文章的最后一个例子中证实这里点。

4.2名称索引和描述符索引---一个方法的签名

    紧接着访问标志(access_flag)后面的两个字节,叫做名称索引(name_index),这两个字节的值是指向了常量池中某个常量池项的索引,该项常量池表示这个方法名称的字符串。

    方法描述索引(descriptor_info)是紧跟在名称索引后面的两个字节,这两个字节中的值跟名称索引中的值性质一样,都是指向了常量池中的某个常量池项,是表示了方法描述符的字符串

    所谓的方法描述,实际上就是指用一个什么药的字符串来描述一个方法。方法描述符的组成如下所示

关于Java虚拟机二三事(七)---类文件结构(下)

    举例:对于如下定义的的greeting()方法,我们来看一下对应的method_info结构体中的名称索引和描述符索引信息是怎样组织的。

[java] view plain copy
  1. public static synchronized final void greeting(){  
  2. }  

     如下图所示,method_info结构体的名称索引中存储了一个索引值x,指向了常量池中的第x项,第 x项表示的是字符串"greeting",即表示该方法名称是"greeting";描述符索引中的y 值指向了常量池的第y项,该项表示字符串"()V",即表示该方法没有参数,返回值是void类型。

            关于Java虚拟机二三事(七)---类文件结构(下)

4.3 属性表集合——记录方法的及其指令和抛出异常等信息

    属性表集合记录了某个方法的一些属性信息,这些信息包括:
  • 这个方法的代码实现,方法的可执行的机器指令
  • 这个方法声明的要抛出的异常信息
  • 这个方法是否被@deprecated注解表示
  • 这个方法是否是编译器自动生成的
 
    属性表(attribute_info)结构体的一般结构如下所示:

            关于Java虚拟机二三事(七)---类文件结构(下)

    

    如上图所示,Exceptions类型的属性表(attribute_info)结构体由以下元素组成:

属性名称索引(attribute_name_index): 占有2个字节,其中的值指向了常量池中的表示“Exceptions”字符串的常量池项

属性长度(attribute_length);它比较特殊,占有4个字节,它的值表示跟在其后面多少个字节表示异常信息。

异常数量(number_of_exceptions):占有2个字节,它的值表示方法声明抛出了多少个异常,即表示跟在气候有多少个异常名称索引。

异常名称索引(exceptions_index_table):占有2个字节,它的值指向了常量池中的某一项,该项是一个CONSTANT_Class_info类型的项,表示这个异常的完全限定名称;

    

Exceptions类型的属性表的长度计算

如果某个方法定义中,没有声明抛出异常,那么,表示该方法的方法表(method_info)结构体中的属性表集合中不会有Exceptions类型的属性表;换句话说,如果方法声明了要抛出的异常,方法表(method_info)结构体中的属性表集合中必然会有Exceptions类型的属性表,并且该属性表中的异常数量不小于1


我们假设异常数量中的值为 N,那么后面的异常名称索引的数量就为N,它们总共占有的字节数为N*2,而异常数量占有2个字节,那么将有下面的这个关系式:


       属性长度(attribute_length)中的值= 2  + 2*异常数量(number_of_exceptions)中的值


       Exceptions类型的属性表(attribute_info的长度=2+4+属性长度(attribute_length)中的值

举例:

    将上面定义的Interface接口类编译成class文件,然后我们查看Interface.class文件,找出方法表集合所在的位置和相应数据,并用javap -v Interface命令查看常量池信息,如下图所示。

            关于Java虚拟机二三事(七)---类文件结构(下)

            

  由于sayHello()方法是在的Interface接口类中声明的,它没有被实现,所以它对应的方法表(method_info)结构体中的属性表集合没有Code类型的属性表

注:

1. 方法计数器(methods_count中的值为0x0001,表明其后的方法表(method_info)就一个,即我们就定义了一个方法,其后会紧跟着一个方法表(method_info)结构体;

2. 方法的访问标志(access_flags的值是0x0401,二进制是00000100 00000001,第6位和第16位是1,对应上面的标志位信息,可以得出它的访问标志符有:ACC_ABSTRACT、ACC_PUBLIC。细心的读者可能会发现,在上面声明的sayHello()方法中并没有声明为abstract类型啊。确实如此,这是因为编译器对于接口内声明的方法自动加上ACC_ABSTRACT标志

3. 名称索引(name_index中的值为0x00050x0005指向了常量池的第5项,第五项表示的字符串为“sayHello”,即表示的方法名称是sayHello

4. 描述符索引(descriptor_index)中的值为0x0006,0x0006指向了常量池中的第6项,第6项表示的字符串为“()V” 表示这个方法的无入参,返回值为void类型

5. 属性表计数器(attribute_count)中的值为0x0001,表示后面的属性表的个数就1个,后面紧跟着一个attribute_info结构体;

6. 属性表(attribute_info中的属性名称索引(attribute_name_index)中的值为0x00070x0007指向了常量池中的第7 项,第 7项指向字符串“Exceptions”,即表示该属性表表示的异常信息;

7. 属性长度(attribute_length中的值为:0x00000004,即后续的4个字节将会被解析成属性值;

8. 异常数量(number_of_exceptions中的值为0x0001,表示这个方法声明抛出的异常个数是1个;

9.异常名称索引(exception_index_table)中的值为0x0008,指向了常量池中的第8项,第8项表示的是CONSTANT_Class_info类型的常量池项,表示“java/lang/Exception”,即表示此方法抛出了java.lang.Exception异常。



    
推荐阅读
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
author-avatar
_cristal_500
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有