JVM笔记1 类文件结构

编译与执行

在这里插入图片描述

类文件结构

  • class文件没有分割符,全是数据,本质上是01010的编码
  • class文件结构逻辑上以8个字节为单位,当数据项超过8个字节时, 则会按照高位在前的方式分割。
  • class文件采用的类似c语言的结构体的方式存储数据,分为两种类型
    • 无符号数:是基本的数据类型, 以u1、 u2、 u4、 u8来分别代表1个字节、 2个字节、 4个字节和8个字节的无符号数, 无符号数可以用来描述数字、 索引引用、 数量值或者按照UTF-8编码构成字符串值。
    • 表:由无符号数和表构成的复合数据类型。整个Class文件就是一张表。
    • 集合:无论是无符号数还是表, 当需要描述同一类型但数量不定的多个数据时, 经常会使用一个前置的容量计数器加若干个连续的数据项的形式, 这时候称这一系列连续的某一类型的数据为某一类型的“集合”。
  • class文件数据中固定/规整的部分,直接使用规定确定死,不固定/变动的部分,采用前置的容量计数器+数据项的形式搞定。
    在这里插入图片描述

常量池

  • 前2个字节表示常量池的常量个数(constant_pool_count),从1开始
  • 每个常量都是一个表
  • 常量分为两类
    • 字面量:文本字符串、 被声明为final的常量值等
    • 符号引用:属于编译原理方面的概念,主要包括
      • 被模块导出或者开放的包(Package)
      • 类和接口的全限定名(Fully Qualified Name)
      • 字段的名称和描述符(Descriptor)
      • 方法的名称和描述符
      • 方法句柄和方法类型(Method Handle、 Method Type、 Invoke Dynamic)
      • 动态调用点和动态常量(Dynamically-Computed Call Site、 Dynamically-Computed Constant)
        java编译时没有连接,而是在类文件被加载到虚拟机中时,类加载的步骤中做的连接,主要是讲常量池中的符号引用替换为具体的内存地址。
  • 我对常量池的理解
    • 常量池描述了啥
      • 类或接口的描述:类/接口全名,包括本类及本类中使用到类,如使用继承了某个类,实现某个接口,使用了某个类的属性或方法等
      • 属性的描述:类/接口全名+属性类型名+属性类型值
      • 方法的描述:类/接口全名+方法名+形参类型名列表+返回值类型
      • 方法句柄等待补充
    • 为了描述这些东西定义一些数据结构,即17中常量类型
  • 常量的表示
    • 常量的类型,即表的结构,共定义了17种,详见表6-3。标志就是类型的码值。除constant_utf8_info(表6-5)外,都是规整的。如constant_class_info(表6-4),一共
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

类信息部分:类的访问标记+类全名+父类全名+实现的接口数+实现接口名列表

在这里插入图片描述

字段表(字段集合)=字段个数+字段表

  • 字段=访问标记(static,final,volatile,transition等)+字段描述(=类全名+字段类型+字段名+字段的值)
  • 字段包括:类字段+实例字段,不包括局部变量
  • 字段表集合中不会列出从父类或者父接口中继承而来的字段
  • 有可能出现原本Java代码之中不存在的字段, 譬如在内部类中为了保持对外部类的访问性, 编译器就会自动添加指向外部类实例的字段。
  • Java语言中字段是无法重载的, 两个字段的数据类型、 修饰符不管是否相同, 都必须使用不一样的名称, 子类可以覆写父类的属性,
  • 对于Class文件格式来讲, 只要两个字段的描述符不是完全相同, 那字段重名就是合法的。
  • 类似的还有方法签名。
    在这里插入图片描述
    在这里插入图片描述

方法表(方法集合)=方法个数+方法表

  • 方法=访问标记(static,final,synchronized,native,abstract等)+方法描述(=类全名+方法名+形参类型列表+返回值列表+方法体的编译后的字节码)
  • java语言中方法签名为类全名+方法名+形参类型列表,不包括返回值,因此仅有返回值不一样不能重载。但是class文件中可以。
  • 不包括父类的方法
  • 有编译器自动生成的方法,常见的如
    • 类构造器:() 内容待补充
    • 实例构造器 :() 内容待补充
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

属性表(属性集合)=属性个数+属性表

  • 常见的属性表类型:
    • 字段相关:字段值
    • 方法相关:方法编译后的字节码指令,异常表,局部变量表(源码局部变量名称与局部变量的映射,非必须),行号表(源码行号与字节码指令行号的映射非必须)
    • 签名:因为编译会泛型擦除,所以需要记录泛型签名
    • 源文件名
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

Code:方法编译后的字节码指令

在这里插入图片描述

  • code=max_stack+max_locals+字节码长度+字节码指令+异常表长度+异常表(是方法抛出的异常而非异常表)+属性表长度+属性表
  • max_stack:代表了操作数栈(Operand Stack) 深度的最大值。 在方法执行的任意时刻, 操作数栈都不会超过这个深度。 虚拟机运行的时候需要根据这个值来分配栈帧(Stack Frame) 中的操作栈深度。
  • max_locals代表了局部变量表所需的存储空间。 在这里, max_locals的单位是变量槽(Slot) , 变量槽是虚拟机为局部变量分配内存所使用的最小单位。 对于byte、 char、 float、 int、 short、 boolean和returnAddress等长度不超过32位的数据类型, 每个局部变量占用一个变量槽, 而double和long这两种64位的数据类型则需要两个变量槽来存放。 方法参数(包括实例方法中的隐藏参数“this”) 、 显式异常处
    理程序的参数(Exception Handler Parameter, 就是try-catch语句中catch块中所定义的异常) 、 方法体中定义的局部变量都需要依赖局部变量表来存放。 注意, 并不是在方法中用了多少个局部变量, 就把这些局部变量所占变量槽数量之和作为max_locals的值, 操作数栈和局部变量表直接决定一个该方法的栈帧所耗费的内存, 不必要的操作数栈深度和变量槽数量会造成内存的浪费。 Java虚拟机的做法是将局部变量表中的变量槽进行重用, 当代码执行超出一个局部变量的作用域时, 这个局部变量所占的变量槽可以被其他局部变量所使用, Javac编译器会根据变量的作用域来分配变量槽给各个变量使用, 根据同时生存的最大局部变量数量和类型计算出max_locals的大小。

异常表

行号表

出错时的堆栈信息中行号,调试时断电

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值