JVM(一)

字节码文件的组成:

基础信息:魔数,字节码文件对应的java版本号,访问表示public final以及父类和接口 

常量池:保存了字符串常量,类或者是接口名,字段名,主要在接口中使用

字段:当前类或者是接口声明的字段信息

方法:当前类或者接口声明的方法信息,字节码指令

属性:指的是类的属性,源码的文件名以及类的列表

字节码文件中常量池的作用:避免相同的内容同时定义节省空间,不仅会使文件变得非常大,况且读取也会非常慢

可以看到字符串的引用存放的是7号的索引,点击常量池的索引,发现又是一个字面量

最后点击25就可以找到最终的字面量了

通过常量池节省字节码文件中的一部分空间,避免同样的数据出现多次

但是为什么字节码文件再进行设计这一块的时候,先通过字符串的引用找到字符串,再来通过字符串找到字面量呢?能不能直接通过字段来找到字面量呢?因为JAVA里面的字符串解析并加载中,需要将String类型加载到字符串常量池中

操作数栈是存放临时数据的地方,两个数相加运算都要放在操作数栈,最终结果都放在操作数栈中,局部变量表是存放方法中局部变量的位置,是在方法中声明的局部变量

下面就是局部变量表,底层是依靠数组来实现的,实际上是依靠你定义变量的顺序来声明数组下标的

istore_i:将操作数栈中的数据取出来放到局部变量表中的对应位置i,那么到底应该放在哪一个位置呢?应该在istrore后面加上一个数组下标,比如说istore_1就会将操作数栈中的内容放到局部变量表中的1号位置,局部变量表中的数据取出来之后,就没了

iload_i:从局部变量表的i位置复制一份取出数放到操作数栈中,最终操作数栈和局部变量表都是会有这个数据的;

iconst_data:将data数据放入操作数栈中

int i=0会拆解出iconst_0和istore_1这两个指令

i_add:将操作数栈中的顶部的两个数据进行相加,并将结果放入到操作数栈中

iinc 1 by 1:将局部变量表中的1号位置加1

JVM是如何执行的?

类加载器:加载class字节玛的内容到内存中

运行时数据区:负责管理JVM使用到的内存,比如说创建对象和销毁对象

方法区:常量,域信息,只有HotSpot虚拟机才有

执行引擎:将字节码文件中的内容解析成机器码,同时使用即时编译优化性能

本地方法接口:调用本地已经编译的方法,比如说虚拟机中已经提供好的C/C++的方法

翻译字节码:针对于字节码的指令进行解释执行

JIT编译器:针对于热点代码进行二次编译(将字节码中的字节码指令编译成机器指令)并将其缓存起来缓存在方法区中

类加载:

1)类加载器子系统负责从文件系统或者是网络中去加载class文件,class文件在文件开头有着特定的文件标识

2)类加载器只是负责class文件的加载,至于它是否可以运行,那么则有执行引擎来决定

3)加载的类信息存放在一块称之为是方法区的内存空间,除了类的信息以外,方法区中还会存放运行时常量池等信息,可能还会包含字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的部分映射)

java虚拟机针对于class文件采用的是按需加载的方式,也就是说当需要使用该类的时候才会将他的class文件加载到内存中生成类对象,而且家在某一个类的class文件的时候,JAVA虚拟机采用的是双亲委派模型,会把请求交给父亲来处理,是一种任务委派模式

在类加载中使用synchronized加锁,向上委托检查,向下加载

1)避免类的重复加载

2)保护程序安全,防止核心API被随意篡改

在JVM中表示两个Class对象是否是同一个需要满足两个条件

1)两个类的完整类名必须完全一致,全限定包名和类名

2)加载这个类的classloader实例对象必须相同

换句话说,在JVM中,即使这两个类对象Class都西昂来源于同一个Class文件,悲痛一个虚拟几所加载,但是只要加载他们的ClassLoader实例对象不同,两个类对象也是不相同的

类加载的相关知识:

类的生命周期描述了一个类加载使用卸载的过程:

加载---链接---初始化----使用----卸载

1)loadding:

a)根据包的全限定包名+类名通过不同的渠道来找到对应的.class文件加载到内存中;

本地的字节码文件+动态代理生成的+网络

b)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,就是将字节码的信息存放到方法区里面,方法区就是用来存放已被加载的类信息,常量,静态变量

c)在方法区中生成这个类的类对象,作为方法区中这个类的各种数据的访问入口

2)链接:

2.1)验证:class文件是以特定的文件符开始的,校验内容是否满足JAVA虚拟机规范;

a)文件格式验证:验证文件是否已特定字符开头,就是以特定的二进制文件开头

b)原信息校验:就是对一些基本信息进行校验,比如说类必须有父类

c)验证程序执行的语义:比如说方法中的指令执行中跳转到不正确的位置

d)符号引用验证:例如说有没有类中访问private修饰的方法

e)版本号检测:如果返回值是true,代表验证成功

class文件的主版本号要大于一个常量,代表所支持的最低版本号,这个常量默认是45代表JDK1.0,JDK的最高版本是52,况且父版本号是0

2.2)准备:为静态变量分配内存并设置初始化值为0值,是默认值的初值,就是防止程序员写出脑残代码,比如说给一个a没有赋初值,如果程序员进行后续操作打印a;

因为final修饰的静态变量,在准备阶段就直接复制初始值了,因为在编译期的时候直接就可以确定值

这里面不会针对于实例变量进行初始化,实例变量会随着对象一起被分配到JAVA的堆里面

2.3)解析:解析所作的操作就是将常量池中的符号引用替换成直接引用

符号引用就是在字节码文件中使用编号来访问字符串常量池中的内容,而直接引用不再使用编号,而是使用内存地址来直接访问具体的数据

3)初始化阶段:执行静态代码块中的代码并且会给静态变量赋初值

其实本质上初始化就是在执行字节码部分的中的clinit部分的字节码指令

iconst:将数字放入到操作数栈中

putstatic:将操作数栈中的数据放到静态变量中

如果将代码进行颠倒,clinit字节码指令执行的顺序和java中编写的顺序是一致的

下面来看一下哪几种方式会导致类的初始化:

1)当访问到一个类的静态变量或者是静态方法,注意变量是final修饰的并且等号右边是常量不会触发初始化

2)调用Class.forName(String className)获取到这个类的类对象的时候

3)new一个该类的对象的时候

4)执行Main方法的当前类

添加-XX:+TraceClassLoading参数可以打印出加载并且初始化的类

下面程序的输出结果是:

执行main函数,况且类加载只会执行一次,所以静态代码块也只会执行一次,先进行类加载DACBCB

clinit方法在特定的条件下不会出现,如下面几种情况是不会执行初始化指令的,也就不会生成clinit方法:

1)没有静态代码块况且没有静态变量赋值语句

2)有静态变量的声明但是没有赋值语句,public static int a;

3)静态变量的定义使用final关键字况且这份变量会在准备阶段直接进行初始化

在上面的情况下不会执行初始化操作

1)直接访问父类的静态变量,不会触发到子类的初始化,子类的clinit方法执行前会先执行父类的clinit方法

2)声明一个类以后,内部至少会存在一个这个类的构造器,也就是一定会出现init方法

访问父类的初始化变量只会初始化父类,因为a只是在父类中,此时打印的是a=1

第五步就是为了防止多个线程多次加载同一个类,从下面的代码中而可以看到类加载中的静态代码块只会执行一次,就相当于是一个加锁的过程,一个类在内存中加载一次即可,方法区在JDK1.8使用的是元空间,会使用直接内存缓存起来了,也就是说JAVA虚拟机在执行类加载的时候只会执行一次,只会调用一次clinit()方法

非法的前向引用:当一个定义的变量出现在静态代码块之后,是可以在静态代码块中赋值的,但是是不可以打印这个静态代码块的

 从下到上查找是否加载过,再从上向下进行记载

 

BootStrap:启动类加载器

 rt.jar:包含着java.lang中的String类这样就可以使用BootStrap类加载器来加载自己所扩展的类

1)将自己写的类打包成jar包

2)将自己写的jar包放在任意一个目录:C:/data

3)使用命令来进行扩展,选择添加虚拟机参数

ext目录:通用但是不重要

Appclassloader:在用户自己写的类和第三方依赖jar包的字节码文件

如何打破双亲委派模型? 
 

1)使用PC寄存器存储字节码指令地址有什么用呢?

为什么使用PC寄存器来记录当前线程执行的地址呢?

CPU需要不停的进行切换各个线程,这时候切换回来之后,就得知道下一条从哪里开始继续执行,JVM的字节码解释器就需要通过改变PC寄存器的值来确定下一条该执行啥样的字节码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/132146.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【新书推荐】AI时代,当程序员遇到ChatGPT,开发效率飞起来!

文章目录 ChatGPT为开发提速一、ChatGPT自动生成代码二、优化代码结构三、自动化测试四、智能推荐五、ChatGPT在开发中的实际应用六、总结 新书推荐《AI时代程序员开发之道:ChatGPT让程序员插上翅膀》内容简介作家简介目录获取方式 ChatGPT为开发提速 人工智能是当…

Elasticsearch 分片内部原理—近实时搜索、持久化变更

目录 一、近实时搜索 refresh API 二、持久化变更 flush API 一、近实时搜索 随着按段(per-segment)搜索的发展,一个新的文档从索引到可被搜索的延迟显著降低了。新文档在几分钟之内即可被检索,但这样还是不够快。 磁盘在这…

在Openresty中使用lua语言向请求浏览器返回请求头User-Agent里边的值

可以参考《Linux学习之Ubuntu 20.04在https://openresty.org下载源码安装Openresty 1.19.3.1,使用systemd管理OpenResty服务》安装Openresty。 然后把下边的内容写入到openresty配置文件/usr/local/openresty/nginx/conf/nginx.conf(根据实际情况进行选…

麻省理工学院与Meta AI共同开发StreamingLLM框架,实现语言模型无限处理长度

🦉 AI新闻 🚀 麻省理工学院与Meta AI共同开发StreamingLLM框架,实现语言模型无限处理长度 摘要:麻省理工学院与Meta AI的研究人员联合研发了一款名为StreamingLLM的框架,解决了大语言模型在RAM与泛化问题上的挑战&am…

Linux文件目录总结

众所周知,Linux系统文件目录是树状结构,如下图所示: 英文缩写的目录下到底存放的是什么文件,善于做归纳总结的逍遥哥哥来解释一下: /bin:bin是Binary的缩写,这个目录存放着最经常使用的命令。 …

设计模式02———建造者模式 c#

首先我们打开一个项目 在这个初始界面我们需要做一些准备工作 建基础通用包 创建一个Plane 重置后 缩放100倍 加一个颜色 更换天空盒(个人喜好) 任务:使用【UI】点击生成6种车零件组装不同类型车 【建造者模式】 首先资源商店下载车模型 将C…

用3-8译码器实现全减器

描述 请使用3-8译码器和必要的逻辑门实现全减器,全减器接口图如下,A是被减数,B是减数,Ci是来自低位的借位,D是差,Co是向高位的借位。 3-8译码器代码如下,可将参考代码添加并例化到本题答案中。 …

B端产品需求分析的思路和方法 4大方面

需求分析对产品成功和客户满意度至关重要,它帮助团队深入了解用户需求,优化用户体验,减少开发中的需求变更,降低开发风险。如果缺乏产品分析,容易造成产品定位不准确,用户体验不佳,不能满足用户…

纯干货,怎样用手机提取歌曲伴奏?

提取歌曲伴奏这个需求还是很大的,要想去掉原声只留伴奏,只要使用音分轨人声分离软件中的【音频提取伴奏】功能就能解决这个问题了,下面就给大家具体演示一下操作步骤! 第一步:打开【音分轨】APP,进入首页点…

微信小程序之本地生活(九宫格)

文章目录 一.创建项目二.配置修改json三.编写WXML四.编写WXSS五.最终效果 一.创建项目 创建新的项目&#xff0c;名称为&#xff1a;本地生活 二.配置修改json 在app.json中删除其他页面 将index改为grid 自动生成新的文件 添加自己的轮播图片 源代码&#xff1a; <!--…

【node】nodemailer配置163、qq等邮件服务指南

上一章 【node】发送邮件及附件简要使用说明 邮箱配置 参数配置参考如下&#xff1a; let transporter nodemailer.createTransport({host: smtp.qq.com,port: 465,secure: true,auth: {user: **********,pass: your-password} });邮箱服务提供商的要求&#xff0c;配置SM…

3.3 数据定义

思维导图&#xff1a; 前言&#xff1a; **核心概念**&#xff1a; - 关系数据库支持**三级模式结构**&#xff1a;模式、外模式、内模式。 - 这些模式中包括了如&#xff1a;模式、表、视图和索引等基本对象。 - SQL的数据定义功能主要包括了模式定义、表定义、视图和索引的定…