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.hqwc.cn/news/59411.html

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

相关文章

C语言第十课----------------扫雷----------数组的经典练手题

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 🎂 作者介绍: 🎂🎂 🎂…

软件验收测试包括几种类型?验收测试报告有什么好处?

在软件开发中,验收测试是软件项目在开发完成后进行的最后一项测试工作。它是确认软件是否满足预期要求,并准备将软件交付用户的核心环节,它可以确保软件的质量和功能符合用户的需求和期望。 一、软件验收测试的类型 软件验收测试可以分为多…

鸿鹄工程项目管理系统em Spring Cloud+Spring Boot+前后端分离构建工程项目管理系统em

​ Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下: 首页 工作台:待办工作、消息通知、预警信息,点击可进入相应的列表 项目进度图表:选择(总体或单个)项目…

Sencha Ext.NET Crack 快速应用程序的正确工具集

Sencha Ext.NET Crack 快速应用程序的正确工具集 Sencha Ext.NET是一个高级的ASP.NET核心组件框架,它包含了强大的跨浏览器Sencha Ext JS库。通过140多个预构建和专业测试的UI组件实现企业级性能和生产效率。Sencha Ext.NET使用尖端的Web技术创建功能强大的Web应用程…

1、如何实现两台电脑之间数据相互读写

一、确保两台电脑在同一个局域网中,可以使用网线【动态配置】进行两台电脑互连。 二、静态配置: 将IP地址和网关设为192.168.0.1,目的是让这台电脑做另一台电脑的网关,子网掩码一点击会自动添加。第二台电脑同样打开设置,此处IP地…

JUC并发编程(JUC核心类、TimeUnit类、原子操作类、CASAQS)附带相关面试题

目录 1.JUC并发编程的核心类 2.TimeUnit(时间单元) 3.原子操作类 4.CAS 、AQS机制 1.JUC并发编程的核心类 虽然java中的多线程有效的提升了程序的效率,但是也引发了一系列可能发生的问题,比如死锁,公平性、资源管理…

python基础3——流程控制

文章目录 一、操作符1.1 比较操作符1.2 逻辑操作符1.3 成员操作符1.4 身份操作符 二、流程控制2.1 条件判断2.2 循环语句2.2.1 for循环2.2.2 while循环 2.3 continue与break语句2.4 文件操作函数 三、函数3.1 定义函数3.2 作用域3.3 闭包3.4 函数装饰器3.5 内建函数 一、操作符…

vuejs 设计与实现 - 渲染器的设计

渲染器与响应式系统的结合 本节,我们暂时将渲染器限定在 DOM 平台。既然渲染器用来渲染真实 DOM 元素,那么严格来说,下面的函数就是一个合格的渲染器: // 渲染器: function renderer(domString, container) {container.innerHTM…

新能源汽车交流充电桩控制主板的功能维度

新能源汽车交流充电桩控制主板的功能维度 交流充电桩主板是电动汽车充电站的关键组件,它负责控制充电过程,保护设备和电网免受电动汽车充电的冲击。它具有控制、保护、检测、报警和记录等功能,可以有效地控制充电过程,保证交流充电…

K8s集群安全机制

1.访问K8s集群的时候,需要经过三个步骤完成具体操作 (1)认证(2)鉴权(授权)(3)准入控制 进行访问的时候,过程中都要经过apiserver,apiserver做统…

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

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

深入探究Spring核心模块

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…