JVM深入 —— JVM的体系架构

前言

        能否真正理解JVM的底层实现原理是进阶Java技术的必由之路,Java通过JVM虚拟机的设计使得Java的延拓性更好,平台无关性是其同时兼顾移动端和服务器端开发的重要特性。在本篇文章中,荔枝将会仔细梳理JVM的体系架构和理论知识,希望能帮助到有需要的小伙伴~~~


文章目录

前言

一、JVM的基本概念

1.1 两种线程和生命周期

1.2 JVM的结构体系

类装载器

运行时数据区

执行引擎

1.3 平台无关性的理解 

二、JVM的内存结构

2.1 方法区

2.2 Java堆

2.3 VM Stack

2.4 本地方法栈

2.5 程序计数器

2.6 本地方法接口JNI 

三、常量池

3.1 编译时常量池

3.2 运行时常量池

3.3 字符串常量池

3.4 常量池的大小限制

总结


一、JVM的基本概念

        JVM(Java Virtual Machine)又被称为Java虚拟机,是Java程序的运行环境。我们知道在Java中程序文件.java会被编译器编译成字节码文件(.class文件),并在JVM中利用解释器解释成机器码执行。JVM是Java实现平台无关性的最关键的组件,只要对应的操作系统中有对应的JVM版本,就可以运行Java的字节码文件,实现一次编译、到处运行的场景。荔枝在看了一些资料后发现大多数的书籍和博客都是以HotSpot虚拟机来介绍的,这里荔枝也就随波逐流地来梳理一下:

1.1 两种线程和生命周期

        JVM是基于线程的,是线程对应的而不是线程共享的。JVM中的线程主要分为两种:守护线程和普通线程。其中守护线程是JVM自己使用的线程,比如垃圾回收机制(GC)就是一个守护线程;而普通线程就是一般的Java线程,只要有线程在执行那么JVM就不会停止。

那么什么时候虚拟机会结束进程呢?

JVM结束生命周期的四种情况

  • 程序正常执行完成后,无普通线程执行
  • 程序执行出现异常报错
  • 执行了System.exit()方法
  • 操作系统出错而导致JVM虚拟机进程终结 

1.2 JVM的结构体系

粗略来分,JVM的内部体系结构分为三部分:类装载器、运行时的数据区和执行引擎。  

类装载器

        类装载器又被称为类加载子系统,在JVM中提供了一套类加载机制。即JVM通过类加载器来将字节码文件加载进入内存空间,获得程序中的类和接口并赋予唯一名称。JVM的类加载器一般来说分为三种:启动类加载器、拓展类加载器和拓展类加载器,通过双亲委派模型来进行类的加载,关于双亲委派模型和类加载机制,这里我们只需要了解有这么一个概念,在荔枝的下一篇文章会详细梳理噢~

除了Java默认提供的三个加载器之外,我们还可以根据自身需求自定义ClassLoader,自定义的类加载器必须继承自 java.lang.ClassLoader 类。

运行时数据区

        运行时的数据区又称内存空间,主要包括了方法区、堆、虚拟机栈、本地方法栈和程序计数器这五部分。Java程序被编译成字节码文件后经过JVM的类加载机制就可以得到开发者定义的相关的类、接口、变量信息和代码缓存,并将这些数据注入内存空间进行储存,之后将程序指令编译成机器指令并交给执行引擎即可。

执行引擎

        执行引擎的主要职责,就是类加载后得到的程序指令集翻译成硬件所支持的指令集格式,然后执行。 在不同的虚拟机实现中,可能会有两种的执行方式:解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码)。虚拟机可以按自身的需求,采用一种或同时采用多种组合的方式来实现执行引擎。但无论内部怎么实现,都要遵循输入的是字节码文件、处理过程是等效字节码解析过程、输出的是执行结果这个JVM规范要求。

1.3 平台无关性的理解 

        我们大致了解了JVM的运行原理和架构体系,但对于JVM的平台无关性这一性质还不是特别明确。我们知道Java在网页端和移动端的开发应用十分广泛,这正是其平台无关性的一个具体的体现。简单理解其实JVM作为一个平台对底层的硬件和上层的类对象和API做了隔离,就相当于套接件,使得我们仅需要根据不同的操作系统找到其对应的JVM版本即可完成程序迁移使用,有点开箱即用的感觉哈哈哈。


二、JVM的内存结构

JVM内存结构中不是所有的数据区都是线程隔离的,其中程序计数器、本地方法栈和VM Stack是线程隔离的;而堆、方法区和本地内存是线程共享的。

2.1 方法区

        存储类的元数据信息、静态变量、即时编译器编译后的代码缓存等。在HotSpot JVM中,方法区被称为"永久代",在Java 8及之后版本中,它被改为"元空间"(Metaspace)。相比于永久代,元空间的好处是会在运行时根据需要动态调整,只要没有超过当前进程可用的内存上限就不会出现溢出的问题,方法区可以被垃圾回收。

元数据信息

包括类的全名、父类名称、类或接口的标识、类型修饰符、所有父接口全名的列表、类型的字段信息、类型的方法信息、静态类信息、类的引用和常量池的信息。

2.2 Java堆

        存储Java对象的内存区域。所有通过new关键字创建的对象都在堆中分配内存。堆区负责存放对象实例,当Java创建一个类的实例对象或者数组时,都会在堆中为新的对象分配内存。需要注意的是:堆空间和方法区是线程共享的,堆的存取是先进先出的,堆内存的大小可以动态分配,并且堆可以由GC机制进行资源回收。

2.3 VM Stack

        在Java栈中只保存基础数据类型对象的引用,注意对象是保存在堆区中的,这里保存的是对象的引用。栈中创建的基本类型数据在超出其作用域之后就会被自动释放掉,不受GC机制的回收管理。当一个线程创建运行的时候,与之对应的栈就创建了,每个栈都是线程隔离滴。每个线程都会建立一个栈,每个栈又包含了若干个栈帧,每个栈帧对应着每个方法的每次调用,栈帧包含了三个部分:局部变量区、操作数栈区和运行环境区。

注意:像String、Integer、Byte、Short、Long、Boolean等等包装类型,它们是存放于堆中的。 

2.4 本地方法栈

        本地方法栈与虚拟机栈类似,但用于执行本地方法(Native方法),存储的也是本地方法的局部变量表,本地方法的操作数栈等信息。本地方法栈中的数据同样也不会被JVM GC管理,而是在其超出作用域之后自动释放。本地方法栈是在程序调用或JVM调用本地方法接口(Native)时候启用。 本地方法都不是使用Java语言编写的,它们可能由C或其他语言编写,本地方法也不由JVM去运行,所以本地方法的运行不受JVM管理。

注意:HotSpot VM将本地方法栈和JVM栈合并了,本地方法栈也会在深度溢出或扩展失败的时候会分别抛出StackOverflowError 和 OutOfMemoryError 异常。

2.5 程序计数器

        程序计数器(Program Counter Register):记录当前线程执行的字节码指令地址,线程切换时,程序计数器也会随之切换,每条线程都会有一个独立的程序计数器。当线程正在执行一个Java方法,程序计数器记录的是正在执行的JVM字节码指令的地址。如果正在执行的是一个Natvie(本地方法),那么这个计数器的值则为空(Underfined)。

2.6 本地方法接口JNI 

        JNI是Java Native interface的缩写,它提供了若干的API实现了Java和其他语言的通信,对于一下其它语言的库或者是函数功能的移植提供了一定的便捷度。但是需要注意的是,一旦使用了JNI,相当于主动放弃了Java的平台无关性这一特性,同时线程也不再是绝对安全的。 


三、常量池

3.1 编译时常量池

Java代码在经过编译器后,会生成一个Class文件,在编译阶段生成的这个常量池储存在Class文件里,它主要存放着 字面量、符号引用等信息,在JVM把Class文件加载完成后,编译时常量池里的数据会存放到运行时常量池中。

3.2 运行时常量池

运行时常量池是在JVM运行时生成的常量池,同时作为方法区(Method Area)的一部分,运行时常量池中存储的是基本类型的数据和对象的引用,与.class文件中的编译时常量池相对应。一般来说,JVM在对字节码文件进行类加载后,会把字节码文件内容里常量池的数据会放入运行时常量池。每一个加载好的Class对象里都会有一个运行时常量池。

3.3 字符串常量池

字符串常量池是常量池的一部分,用于存放字符串字面量。在JDK7之后,字符串常量池从方法区迁移到了堆区,它的底层实现可以理解为是一个HashTable。Java虚拟机中只会存在一份字符串常量池。字符串常量池里,存放的数据可以是引用也可以是对象实例本身。字符串常量池同时也具备运行时常量池动态性的特征,它支持运行期间将新的常量放入池中

注意:字符串常量池中的字符串是不可变的,这意味着一旦创建了一个字符串对象,它的值就不能被修改。

3.4 常量池的大小限制

  • 在JVM规范中,常量池的大小是一个ushort类型(无符号16位整数),因此常量池的最大索引是65535。
  • 当常量池中的项超过该限制时,JVM会抛出"Constant pool is too large"的错误。

总结

        在这篇文章中,荔枝主要梳理了有关JVM体系结构的知识,明确JVM的组成和基本运行原理。对于类加载机制中的双亲委派模型和GC垃圾回收机制荔枝也会在后续的文章中详细的梳理和总结。希望能帮助到有需要的小伙伴哈哈哈~~~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

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

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

相关文章

Dubbo+Zookeeper使用

说明:Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。 本文介绍Dubbo的简单使用及一些Dubbo功能特性,注册中心使用的是ZooKeeper,可在…

驱动工作原理

驱动原理 在Linux操作系统中,硬件驱动程序中实现对硬件直接操作,而用户空间,通过通用的系统调用接口(open() 打开相应的驱动设备,ioctl()控制相应的功能等),实现对硬件操作,应用程序没有直接操作…

树的层次遍历

层次遍历简介 广度优先在面试里出现的频率非常高,整体属于简单题。而广度优先遍历又叫做层次遍历,基本过程如下: 层次遍历就是从根节点开始,先访问根节点下面一层全部元素,再访问之后的层次,类似金字塔一样…

【Uniapp 的APP热更新】

Uniapp 的APP热更新功能依赖于其打包工具 HBuilder,具体步骤如下: 1. 在 HBuilder 中构建并打包出应用程序 具体步骤: 1.点击发行,点击制作wgt包 2.根据需求修改文件储存路径和其他配置,点击确定 3.等待打包完成&a…

Rust中的高吞吐量流处理

本篇文章主要介绍了Rust中流处理的概念、方法和优化。作者不仅介绍了流处理的基本概念以及Rust中常用的流处理库,还使用这些库实现了一个流处理程序。 最后,作者介绍了如何通过测量空闲和阻塞时间来优化流处理程序的性能,并将这些内容同步至…

Android 实现账号诊断动画效果,逐条检测对应的项目

Dialog中的项目 逐条检测效果&#xff1a; 依赖库&#xff1a; implementation com.github.li-xiaojun:XPopup:2.9.19 implementation com.blankj:utilcodex:1.31.1 implementation com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.101、item_account_check.xml <…

PictureBox基本使用

作用&#xff1a;展示图片&#xff0c;同时也具有click属性&#xff0c;可用充当按钮功能。 常用属性&#xff1a; 设置图片 设置图片的填充模式 常用事件&#xff1a; 后台代码示范&#xff1a;增加点击事件 private void pictureBox1_Click(object sender, EventArgs e){//…

【CodeWhisperer】亚马逊版代码生成工具

大家好&#xff0c;我是荷逸&#xff0c;今天给大家带来的是代码生成工具【CodeWhisperer】 CodeWhisperer简介 CodeWhisperer是亚⻢逊出品的一款基于机器学习的通用代码生成器&#xff0c;可实时提供代码建议。 在编写代码时&#xff0c;它会自动根据我们现有的代码和注释生…

Java中「Future」接口详解

一、背景 在系统中&#xff0c;异步执行任务&#xff0c;是很常见的功能逻辑&#xff0c;但是在不同的场景中&#xff0c;又存在很多细节差异&#xff1b; 有的任务只强调「执行过程」&#xff0c;并不需要追溯任务自身的「执行结果」&#xff0c;这里并不是指对系统和业务产…

JDK, JRE和JVM之间的区别和联系

JDK, JRE和JVM是与Java编程语言相关的三个重要的概念&#xff0c;它们分别代表Java Development Kit&#xff08;Java开发工具包&#xff09;、Java Runtime Environment&#xff08;Java运行时环境&#xff09;和Java虚拟机&#xff08;Java Virtual Machine&#xff09;。它们…

大数据课程G2——Hbase的基本架构

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 掌握Hbase的基本架构; ⚪ 掌握Hbase的读写流程; ⚪ 掌握Hbase的设计与优化; 一、基本架构 1. HRegion 1. 在HBase中,会将一个表从行键方向上进行切分,切分成1个或者多个HRegion。 …

C#利用自定义特性以及反射,来提大型项目的开发的效率

在大型项目的开发过程中&#xff0c;需要多人协同工作&#xff0c;来加速项目完成进度。 比如一个软件有100个form&#xff0c;分给100个人来写&#xff0c;每个人完成自己的Form.cs的编写之后&#xff0c;要在Mainform调用自己写的Form。 如果按照正常的Form form1 new For…

MIT 6.824 -- MapReduce -- 01

MIT 6.824 -- MapReduce -- 01 引言抽象和实现可扩展性可用性(容错性)一致性MapReduceMap函数和Reduce函数疑问 课程b站视频地址: MIT 6.824 Distributed Systems Spring 2020 分布式系统 推荐伴读读物: 极客时间 – 大数据经典论文解读DDIA – 数据密集型应用大数据相关论文…

【具身智能】系列论文解读(CoWs on PASTURE VoxPoser Relational Pose Diffusion)

0. My Conclusion CoWs on PASTURE&#xff1a; 擅长零样本的视觉语言对象导航&#xff0c;主要解决了LLM辅助下的任务级动作执行任务VoxPoser&#xff1a; 擅长设计一些未预定义的动作轨迹&#xff0c;主要解决了LLM辅助下的动作轨迹设计任务Relational Pose Diffusion&#…

Packet Tracer - 将路由器连接到 LAN

Packet Tracer - 将路由器连接到 LAN 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 R1 G0/0 192.168.10.1 255.255.255.0 N/A G0/1 192.168.11.1 255.255.255.0 N/A S0/0/0 (DCE) 209.165.200.225 255.255.255.252 N/A R2 G0/0 10.1.1.1 255.255.255…

概率论与数理统计复习总结3

概率论与数理统计复习总结&#xff0c;仅供笔者复习使用&#xff0c;参考教材&#xff1a; 《概率论与数理统计》/ 荣腾中主编. — 第 2 版. 高等教育出版社《2024高途考研数学——概率基础精讲》王喆 概率论与数理统计实际上是两个互补的分支&#xff1a;概率论 在 已知随机…

Kernel Exception导致手机重启案例分析

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、高温触发 Kernel Exception 重启问题二、解决方案三、提高电池温度方案 一、 高温触发 Kernel Exception 重启问题 手机 电池温度 默认60度以上高温…

CBCGPRibbon 添加背景图片

resource.h中声明资源的ID&#xff1a;ID_RIBBON_BACKIMAGE rc文件中添加png图片路径&#xff1a; ID_RIBBON_BACKIMAGE PNG DISCARDABLE "res\\bkribbon.png" 代码中添加下测&#xff1a; //添加背景图片 m_wndRibbonBar.SetBackgroundImage(ID_RIB…

C语言单链表OJ题(较易)

一、移除链表元素 leetcode链接 题目描述&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 思路&#xff1a; 正常遍历&#xff0c;找到value的值与题目中相同的结点去fr…

第5集丨Vue 江湖 —— 监视属性/侦听属性

目录 一、基本使用1.1 watch配置监视1.2 vm.$watch动态监视1.3 深度监视(deep watch)1.4 简写形式 二、computed和watch的对比2.1 使用watch实现setTimeout操作2.2 用computed无法实现setTimeout 三、其他注意事项3.1 vue devtools的bug3.2 xxxyyy格式3.3 将window传入data中 V…