JVM-从字节码聊起

在这里插入图片描述

• u4:cafebabe —魔数

魔数:所以的由java编译器而成的class文件的前4个字节都是"oxCAFABABE"

• u2:minor_version—JDK次版本号------ 00 00
• u2:major_version—JDK主版本号------ 00 34
• u2:constant_pool_count—常量池数量—00 19

这个容量计数是从1而不是0开始的,ox0019—>25,这代表常量池中有21个常量,索引范围为1~21
设计者将第0项常量空出来是有特殊考虑的,这样做的目的在于满足后面某些指向常量池的索引值的数据在特定
情况下需要表达“不引用任何一个常量池项目”的含义,这种情况就可以把索引值置为0来表示

• cp_info:constan_pool—常量池表---------数量: constant_pool_count-1
• u2 access_flags-----------访问标志---------------------1
• u2 this_class--------------类引用------------------------1
• u2 super_class------------父类引用---------------------1
• u2 interfaces_count------接口数量----------------------1
• u2 interfaces-------------接口数组------------- interfaces_count
• u2 fields_count-----------字段数量--------------------- 1
• field_info fields------------字段表--------------fields_count
• u2 methods_count---------方法数量--------------------1
• method_info methods-------方法表------------methods_count
• u2 attributes_count----------属性数量--------------- 1
• attribute_info attributes------属性表----------- attributes_count
在这里插入图片描述

cp_info的结构

在这里插入图片描述

tag对应图

在这里插入图片描述

注意:
tag:1~6是字面量型结构体,info存储的是字面量值
tag:7~18是引用型结构体,info存储的是某些字面量型结构体的索引

int和float数据类型的常量在常量池中的存储

在这里插入图片描述

如果有多个相同的常量,只有一个是存储一个结构体,其他的都指向这个索引值
关于int类型是否放入常量池问题:

	①如果final int----->会被放入常量池
	②如果int的值在short的范围内(-32768~32767)----->则会直接放入栈中
	③如果int的值不在short的范围内------>会被放入常量池中

Long和double的数据类型在常量池中的存储

在这里插入图片描述

如果有多个相同的常量,只有一个是存储一个结构体,其他的都指向这个索引值

关于double和long的原子性问题

由于long和double有 high_bytes4个字节,和low_bytes4个字节组成,如果用32位的jvm会存在原子性问题,需要用volatile进行修饰才行.64位的jvm不存在此问题

String类型的字符串常量在常量池的存储(引用性结构体)

在这里插入图片描述

我们写的String s="";便会存储成CONSTANT_STRING_info,其结构中的string_index会指向某个CONSTANT_UTF8_info的索引,CONSTANT_UTF8_info存储的是具体的string字符串

CONSTANT_Utf8_info结构体(所有的引用型结构体都会指CONSTANT_Utf8_info结构体)

在这里插入图片描述
常量池中的其他info结构体与CONSTANT_String_info一样都指向CONSTANT_Utf8_info,只是内容不同

类文件中定义的类名和类中使用到的类在常量池中的存储 类的完全限定名和二进制形式的完全限定名
在某个Java源码中,我们会使用很多个类,比如我们定义了一个 ClassTest的类,并把它放到 comjvm 包下,则
ClassTest类的完全限定名为com.jvm.ClassTest,将JVM编译器将
类编译成class文件后,此完全限定名在class文件中,是以二进制形式的完全限定名存储的,即它 会把完全限定符的".“换成”/"
,即在class文件中存储的 ClassTest类的完全限定名称
是"com/jvm/ClassTest"。因为这种形式的完全限定名是放在了class二进制形式的字节码 文件中,所以就称之为
二进制形式的完全限定名。

我们定义一个很简单的ClassTest类,来看一下常量池是怎么对类的完全限定名进行存储的

package com.jvm; 
import  java.util.Date; 
public class ClassTest { 
private Date date =new Date(); 
} 

将Java源码编译成ClassTest.class文件后,在此文件的目录下执行 javap -v ClassTest 命令,会看到如下的常量池信息的轮廓:
在这里插入图片描述

对于某个类而言,其class文件中至少要有两个CONSTANT_Class_info常量池项,用来表示自己的类
信息和其父类信息。(除了java.lang.Object类除外,其他的任何类都会默认继承自
java.lang.Object)如果类声明实现了某些接口,那么接口的信息也会生成对应的 CONSTANT_Class_info常量池项。

符号引用和直接引用

符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中

org.simple.People类 引用了 org.simple.Language类 ,在编译时People类并不知道Language
类的实际内存地址,因此只能使用符号 org.simple.Language
(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址。

直接引用是和虚拟机的布局相关的

直接引用可以是:

  1. 直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向 方法区的指针)
  2. 相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
  3. 一个能间接定位到目标的句柄

引用替换的时机

符号引用替换为直接引用的操作发生在类加载过程(加载 -> 连接(验证、准备、解析) ->
初始化)中的解析阶段,会将符号引用转换(替换)为对应的直接引用,放入运行时常量池中。

访问标识(常量池之后)

在常量池结束之后,紧接着的两个字节代表访问标志(access_flags),这个标志用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等
在这里插入图片描述

类引用和父类引用

类索引、父类索引和接口索引集合都按顺序排列在访问标志之后,类索引和父类索引用两个u2类型的索引值表示,它们各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info类型的常量中的索引值可以找到定义在CONSTANT_Utf8_info类型的常量中的全限定名字符串

接口计数器和接口数组, 字段表集合等都同上大同小异

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值