深入浅出Java虚拟机

文章目录

  • 总体图
  • 类装载子系统
    • 一、类的加载过程
      • 一、加载
      • 二、链接
      • 三、初始化
    • 二、类的加载器
  • 运行时数据区
    • 一、程序计数器(ProgramCounter)
    • 二、虚拟机栈( Java Stack )
    • 三、本地方法栈( Native Method Stack )
    • 四、堆内存(Direct Memory)
    • 五、方法区(Method Area)
  • 执行引擎区域
    • 一、Java执行引擎
    • 二、本地方法接口JNI

在这里插入图片描述

总体图

这个架构可以分成三层看:

  • 最上层:javac编译器将编译好的字节码class文件,通过java 类装载器执行机制,把对象或class文件存放在 jvm划分内存区域
  • 中间层:称为Runtime Data Area,主要是在Java代码运行时用于存放数据的,从左至右为方法区(永久代、元数据区)、堆(共享,GC回收对象区域)、栈、程序计数器、寄存器、本地方法栈(私有)
  • 最下层:解释器、JIT(just in time)编译器和 GC(Garbage Collection,垃圾回收器)

类装载子系统

在这里插入图片描述

一、类的加载过程

一、加载

  • 通过一个类的全限定名获取定义此类的二进制字节流
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

二、链接

验证(Verify):

  • 目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全。
  • 主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。

准备(Prepare):

  • 为类变量分配内存并且设置该类变量的默认初始值,即零值。
  • 这里不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显式初始化;
  • 这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中

解析(Resolve):

  • 将常量池内的符号引用转换为直接引用的过程。
  • 事实上,解析操作往往会伴随着JVM在执行完初始化之后再执行。
  • 符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《java虚拟机规范》的Class文件格式中。
  • 在解析阶段,jvm根据字符串的内容找到内存区域中相应的地址,然后把符号引用替换成直接指向目标的指针、句柄、偏移量等,这些直接指向目标的指针、句柄、偏移量就被成为直接引用。
  • 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。

三、初始化

  • 初始化阶段就是执行类构造器方法()的过程。
  • 此方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。
  • 构造器方法中指令按语句在源文件中出现的顺序执行。
  • ()不同于类的构造器。(关联:构造器是虚拟机视角下的())
  • 若该类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕。
  • 虚拟机必须保证一个类的()方法在多线程下被同步加锁。

二、类的加载器

JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)

在这里插入图片描述

双亲委派机制

ClassLoader类使用了委托模型来寻找类和资源,ClassLoader的每一个实例都会有一个与之关联的父ClassLoader,当ClassLoader被要求寻找一个类或者资源的时候,ClassLoader实例在自身尝试寻找类或者资源之前会委托它的父类加载器去完成。虚拟机内建的类加载器,称之为启动类加载器,是没有父加载器的,但是可以作为一个类加载器的父类加载器

破坏双亲委派机制

  • JDK1.2前的代码无法再以技术手段避免loadClass()被子类覆盖的可能性
  • JNDI(资源查找和管理)在应用程序的ClassPath下的JNDI服务提供者SPI接口,由父类加载器去请求子类加载器完成类加载的行为
  • 代码热替换(Hot Swap)、模块热部署(Hot Deployment)

自定义类加载器

  • 隔离加载类,避免类冲突
  • 修改类加载的方式,根据实际情况在某个时间点按需动态加载
  • 扩展加载源:网络、数据库、机顶盒
  • 防止源码泄漏

沙箱安全机制

Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有限隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样

运行时数据区

在这里插入图片描述

一、程序计数器(ProgramCounter)

  • 具有线程隔离性
  • 占用的内存空间非常小,可以忽略不计
  • java虚拟机规范中唯一一个没有规定任何OutofMemeryError的区域
  • 程序执行的时候,程序计数器是有值的,其记录的是程序正在执行的字节码的地址

二、虚拟机栈( Java Stack )

在这里插入图片描述

一个线程对应一个栈,一个栈对应多个方法栈帧, 栈帧包含局部变量表、操作数栈、动态连接、方法出口等

int i = 8;
i = i++;
 0 bipush 8	压栈
 2 istore_1	赋值出栈
 3 iload_1	压栈
 4 iinc 1 by 1	+1
 7 istore_1	赋值出栈
 8 getstatic #3 <java/lang/System.out>
11 iload_1	
12 invokevirtual #4 <java/io/PrintStream.println>
15 return
  1. 基于寄存器的指令集

  2. 基于栈的指令集

    1. HotSpot中的Local Variable table = JVM中的寄存器
  3. JVM指令主要分为:本地变量表到操作数栈类指令、操作数栈到本地变量表类指令、常数到操作数栈类指令、将数组指定索引的数组推送至操作数栈类指令、将操作数栈数存储到数组指定索引类指令、操作数栈其他相关类指令、运算相关类指令、条件转移类指令、类和数组类指令和其他指令。

  4. i开头的指令操作数类型是integer类型,l开头的指令操作数类型是long类型,f开头的指令操作数类型是float类型,d开头的指令操作数类型是double,a开头的指令操作数类型是引用类型(reference)。

  5. load类指令将数据从本地变量表加载到操作数栈,store类指令将数据从操作数栈存储到本地变量表中。其他的指令主要用于操作数栈。

三、本地方法栈( Native Method Stack )

  • Java虚拟机栈于管理Java方法的调用,而本地方法栈用于管理本地方法的调用
  • 本地方法栈,也是线程私有的。
  • 允许被实现成固定或者是可动态扩展的内存大小
    • 如果线程请求分配的栈容量超过本地方法栈允许的最大容量,Java虚拟机将会抛出一个stackoverflowError 异常。
    • 如果本地方法栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的本地方法栈,那么Java虚拟机将会抛出一个outofMemoryError异常。
  • 本地方法一般是使用C语言实现的。
  • 它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。

注意点:

当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。它和虚拟机拥有同样的权限。

  • 本地方法可以通过本地方法接口来访问虚拟机内部的运行时数据区
  • 它甚至可以直接使用本地处理器中的寄存器
  • 直接从本地内存的堆中分配任意数量的内存

并不是所有的JVM都支持本地方法。因为Java虚拟机规范并没有明确要求本地方法栈的使用语言、具体实现方式、数据结构等。如果JVM产品不打算支持native方法,也可以无需实现本地方法栈。

在Hotspot JVM中,直接将本地方法栈和虚拟机栈合二为一

四、堆内存(Direct Memory)

  • 年轻代:新对象和没达到一定年龄的对象都在年轻代。
  • 老年代:被长时间使用的对象,内存空间应该要比年轻代更大。
  • 元空间:元空间不在虚拟机设置的内存中,而是使用本地内存。是对方法区的一种实现
  • 直接内存: Java堆外的、直接向系统申请的内存区间
  • StringTable
    • Java 6及以前,字符串常量池存放在永久代。
    • Java 7 中 Oracle 的工程师对字符串池的逻辑做了很大的改变,即将字符串常量池的位置调整到Java堆内。
    • Java 8 中,字符串常量仍然在堆

五、方法区(Method Area)

  • 存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等

  • 方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。

    • 该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。
    • 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、 JSP的重加载等,否则通常是很难达成的。
    • 该类对应的java. lang. Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
  • 方法区在JVM启动的时候被创建,并且它实际的物理内存空间中和Java堆区一样都可以是不连续的。

  • 方法区的小大,跟堆空间一样,可以选择固定大小或者可扩展

  • 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:java.lang.OutOfMenoryError:Metaspace

  • 关闭JVM就会释放这个区域的内存

变化

JDK1.6及其以前:有永久代,静态变量存放在永久代上。

JDK1.7:有永久代,但已经逐步“去永久代”,字符串常量池、静态变量移除,保存在堆中。

JDK1.8及其之后:无永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但字符串常量池、静态变量仍在堆。

执行引擎区域

在这里插入图片描述

一、Java执行引擎

在这里插入图片描述

执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者

  1. 执行引擎在执行的过程中究竟需要执行什么样的字节码指令完全依赖于PC寄存器。
  2. 每当执行完一项指令操作后,PC寄存器就会更新下一条需要被执行的指令地址。
  3. 当然方法在执行的过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在Java堆区中的对象实例信息,以及通过对象头中的元数据指针定位到目标对象的类型信息。

JIT (Just In Time Compiler)编译器(即时编译器):是虚拟机将源代码直接编译成和本地机器平台相关的机器语言

解释器:当Java虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“翻译”为对应平台的本地机器指令执行

HotSpot VM中,解释器主要由Interpreter模块和Code模块构成。

  • Interpreter模块:实现了解释器的核心功能
  • Code模块:用于管理HotSpot VM在运行时生成的本地机器指令

热点代码及探测方式

  • 热点代码:一行代码或者一段逻辑被多次频繁调用,JIT会将其编译成机器码指令,提高代码运行效率

  • 热点探测功能 :JIT编译器通过一个阈值才会将这些“热点代码”编译为本地机器指令执行

  • 采用基于计数器的热点探测

  • 方法调用计数器用于统计方法的调用次数

  • 回边计数器则用于统计循环体执行的循环次数

HotSpot VM 可以设置程序执行方式

  • -Xint: 完全采用解释器模式执行程序;
  • -Xcomp: 完全采用即时编译器模式执行程序。如果即时编译出现问题,解释器会介入执行。
  • -Xmixed:采用解释器+即时编译器的混合模式共同执行程序

HotSpot VM 中的JIT分类

  • client: 指定Java虚拟机运行在Client模式下,并使用C1编译器;
    C1编译器会对字节码进行简单和可靠的优化,耗时短。以达到更快的编译速度。
  • server: 指定Java虚拟机运行在Server模式下,并使用C2编译器。
    C2进行耗时较长的优化,以及激进优化。但优化的代码执行效率更高
  • Graal编译器: 使用了 Graal 编译器技术,采用 Java 写的 JIT 编译器, 让 Java 可以在一个运行期内,同时使用多种语言
  • AOT编译器:jdk9引入的静态提前编译器, 在程序运行之前,便将字节码转换为机器码的过程

二、本地方法接口JNI

JNI:Java Native Interface 本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序

  • Java诞生的时候C、C++横行,为了立足,必须要能调用C、C++的程序
  • 于是在内存区域中专门开辟了一块标记区域:Native Method Stack,登记Native方法
  • 最终在执行引擎执行的的时候通过JNI(本地方法接口)加载本地方法库的方法

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机的中断相关知识

51单片机的中断相关知识点 一、中断概念和功能 概念 程序执行过程中CPU会遇到一些特殊情况&#xff0c;是正在执行的程序被“中断”&#xff0c;cpu中止原来正在执行的程序&#xff0c;转到处理异常情况或特殊事件的程序去执行&#xff0c;结束后再返回到原被中止的程序处(断…

【Android12】Android Framework系列---tombstone墓碑生成机制

tombstone墓碑生成机制 Android中程序在运行时会遇到各种各样的问题&#xff0c;相应的就会产生各种异常信号&#xff0c;比如常见的异常信号 Singal 11&#xff1a;Segmentation fault表示无效的地址进行了操作&#xff0c;比如内存越界、空指针调用等。 Android中在进程(主要…

Screenshot-to-code开源项目mac上实践

github上的开源项目&#xff0c;看介绍可以将设计ui图片转换为 HTML 和 CSS 源码地址&#xff1a; GitCode - 开发者的代码家园 我的mac安装了2.7和3.11&#xff0c;就用3吧直接上代码 安装 pip3 install keras tensorflow pillow h5py jupyter 报错 ERROR: Could not in…

TCP服务器的编写(下)

我们现在开始对我们的客户端开始封装 我们的客户端&#xff0c;创建完套接字&#xff0c;需不需要bind呢&#xff1f;&#xff1f; 当然是不需要的&#xff0c;你本身是一个客户端&#xff0c;其他人写的应用也可能是客户端&#xff0c;如果我们bind&#xff0c;一定意味着我们…

Javaweb之Mybatis入门的详细解析

Mybatis入门 前言 在前面我们学习MySQL数据库时&#xff0c;都是利用图形化客户端工具(如&#xff1a;idea、datagrip)&#xff0c;来操作数据库的。 在客户端工具中&#xff0c;编写增删改查的SQL语句&#xff0c;发给MySQL数据库管理系统&#xff0c;由数据库管理系统执行S…

CentOS7安装部署Zookeeper

文章目录 CentOS7安装部署Zookeeper一、前言1.简介2.架构3.集群角色4.特点5.环境 二、正文1.部署服务器2.基础环境1&#xff09;主机名2&#xff09;Hosts文件3&#xff09;关闭防火墙4&#xff09;JDK 安装部署 3.单机部署1&#xff09;下载和解压2&#xff09;配置文件3&…

linux文件夹介绍

在linux内核文件夹下面存在着许多文件夹&#xff0c;那么那些文件夹是什么用处呢&#xff0c;下面将为你介绍。 (1)documentation 这个文件夹下没有内核代码&#xff0c;仅仅有一套实用的文档&#xff0c;但这些文档的质量不一。比如内核文档的文件系统&#xff0c;在该文件夹下…

动态路由传参与查询参数传参详情

动态路由传参 路由规则path :/article/:aid 组件获取参数: this. $route. params.aid 如果想要所有的值&#xff0c;就用this. $route. params 注意&#xff1a;这两个必须匹配 如果是多个参数&#xff0c;path :/article/:aid/:name就写两个参数 接收方式一&#xff1a; 在…

Javaweb-servlet

一、servlet入门 1.Servlet介绍 (1)什么是Servlet Servlet是Server Applet的简称&#xff0c;是用Java编写的是运行在 Web 服务器上的程序&#xff0c;它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet&#…

Python武器库开发-武器库篇之Git的分支使用(三十九)

武器库篇之Git的分支使用(三十九) Git分支是一种用于在项目中并行开发和管理代码的功能。分支允许开发人员在不干扰主要代码的情况下创建新的代码版本&#xff0c;以便尝试新功能、修复错误或独立开发功能。一般正常情况下&#xff0c;开发人员开发一个软件&#xff0c;会有两…

C#使用条件语句判断用户登录身份

目录 一、示例 二、生成 利用条件语句判断用户登录身份&#xff0c;根据用户登录身份的不同&#xff0c;给予相应的操作权限。 一、示例 主要用if语句及ComboBox控件。其中&#xff0c;ComboBox是窗体中的下拉列表控件&#xff0c;在使用ComboBox控件前&#xff0c;可以先向…

数据结构与算法(五)

文章目录 数据结构与算法(五)33 与哈希函数有关的结构33.1 哈希函数33.2 布隆过滤器33.3 一致性哈希34 资源限制类题目的解题套路34.1 1G内存40亿个无符号整数的文件中找到出现次数最多的数34.2 内存限制为3KB,但是只用找到一个没出现过的数34.3 100亿个URL的大文件中找出其…

《深入理解JAVA虚拟机笔记》并发与线程安全原理

除了增加高速缓存之外&#xff0c;为了使处理器内部的运算单元能尽量被充分利用&#xff0c;处理器可能对输入代码进行乱序执行&#xff08;Out-Of-Order Execution&#xff09;优化。处理器会在计算之后将乱序执行的结果重组&#xff0c;保证该结果与顺序执行的结果一致&#…

分库分表之Mycat应用学习四

4 分片策略详解 分片的目标是将大量数据和访问请求均匀分布在多个节点上&#xff0c;通过这种方式提升数 据服务的存储和负载能力。 4.1 Mycat 分片策略详解 总体上分为连续分片和离散分片&#xff0c;还有一种是连续分片和离散分片的结合&#xff0c;例如先 范围后取模。 …

深度学习中的感知机

感知机是一种判别模型&#xff0c;其目标是求得一个能够将数据集中的正实例点和负实例点完全分开的分离超平面。 感知机在1957年由弗兰克罗森布拉特提出&#xff0c;是支持向量机和神经网络的基础。感知机是一种二类分类的线性分类模型&#xff0c;输入为实例的特征向量&#x…

TOGAF架构开发方法

TOGAF针对架构开发方法定义了一系列阶段和步骤&#xff0c;这些阶段和步骤对架构的迭代过程进行了详细、标准的描述。 企业架构的项目过程 一、预备阶段&#xff08;Preliminary&#xff09; 1、目标 预备阶段的目标是&#xff1a; 对组织的背景和环境进行审查&#xff08;调…

适应变化:动态预测在机器学习中的作用

一、介绍 机器学习 (ML) 中的动态预测是指随着新数据的出现而不断更新预测的方法。这种方法在从医疗保健到金融等各个领域越来越重要&#xff0c;其中实时数据分析和最新预测可以带来更好的决策和结果。在本文中&#xff0c;我将讨论机器学习中动态预测的概念、其优势、挑战以及…

Fiddler Classic 实现汉化

安装&#xff1a;https://www.telerik.com/fiddler/fiddler-classichttps://www.telerik.com/fiddler/fiddler-classic 汉化 链接&#xff1a;https://pan.baidu.com/s/1wWgVqrXlh0Gjpbwlg6pPNA 提取码&#xff1a;xq9t 下载到本地之后&#xff0c;得到了两个文件 FdToChinese…

【jdk与tomcat配置文件夹共享防火墙设置(入站出站规则)】

目录 一、jdk与tomcat配置 1.1 jdk配置 1.2 tomcat配置 二、文件夹共享 2.1 为什么需要配置文件夹共享功能 2.2 操作步骤 2.2.1 高级共享 2.2.2 普通共享 2.3 区别 三、防火墙设置&#xff08;入站规则&出站规则&#xff09; 3.1 入站规则跟出站规则 3.2 案例…

ssrf之gopher协议的使用和配置,以及需要注意的细节

gopher协议 目录 gopher协议 &#xff08;1&#xff09;安装一个cn &#xff08;2&#xff09;使用Gopher协议发送一个请求&#xff0c;环境为&#xff1a;nc起一个监听&#xff0c;curl发送gopher请求 &#xff08;3&#xff09;使用curl发送http请求&#xff0c;命令为 …
最新文章