深入理解Java虚拟机(JVM)

引言

Java虚拟机(JVM)是Java平台的核心组件,它负责将Java字节码转换成平台特定的机器指令,并在相应的硬件和操作系统上执行。JVM的引入使得Java语言具有“一次编写,到处运行”的跨平台特性。本文将深入探讨JVM的基本结构、内存模型、关键技术以及性能优化等方面的内容。

一、JVM的基本结构

JVM主要由类加载器、运行时数据区、执行引擎、垃圾收集器和本地接口几部分组成。
JVM的基本结构

  1. 类加载器:负责加载Java类的字节码到JVM中,并将其转换为可以被JVM执行的数据结构。
  2. 运行时数据区:包括方法区、堆、Java栈、程序计数器以及本地方法栈。这些区域在JVM进程启动时创建,并在JVM进程结束时销毁。
  3. 执行引擎:负责读取Java字节码,并对其进行解释或即时编译(JIT),生成本地机器代码并执行。
  4. 垃圾收集器:负责自动回收不再使用的内存空间,防止内存泄漏和溢出。
  5. 本地接口:负责与本地方法库进行交互,允许Java代码调用本地代码(如C、C++等)或被本地代码调用。

运行流程 :首先通过编译器把 Java 代码转换成字节码,类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

二、JVM的内存模型

JVM(Java Virtual Machine)的内存模型是Java程序运行时管理内存的一套规则。它定义了Java程序在JVM中如何通过内存来交互和操作。以下是对JVM内存模型的详细描述:

  1. 堆(Heap)
    • 堆是JVM中最大的一块内存区域,用于存储对象实例以及数组。几乎所有的Java对象实例都在堆上分配。
    • java堆是垃圾收集器管理的主要区域,因此也被成为“GC堆”。堆内存可以进一步细分为新生代和老年代。新生代主要用于存储新创建的对象,而老年代则用于存储长时间存活的对象。新生代又可以分为Eden区、From Survivor区和To Survivor区。
    • 根据Java虚拟机规范的规定,java堆可以处于物理上不连续的内存空间中。当前主流的虚拟机都是可扩展的(通过 -Xmx 和 -Xms 控制)。如果堆中没有内存可以完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
  2. 方法区(Method Area)(也称为元空间(Metaspace)在JDK 8及以后版本):
    • 方法区存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
    • 在JDK 8之前,方法区被称为永久代(PermGen),但由于永久代的大小是固定的,容易引发OutOfMemoryError,因此在JDK 8中,方法区被元空间所取代,元空间使用的是直接内存,受本机总内存限制。
    • 直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现,所以我们放到这里一起讲解。我的理解就是直接内存是基于物理内存和Java虚拟机内存的中间内存
  3. 虚拟机栈(Java Stack)(也称为线程栈):
    • 虚拟机栈是每个线程私有的内存区域,用于存储局部变量、操作数栈、动态链接和方法出口等信息。
    • 当一个方法被调用时,JVM会为该方法的局部变量在栈上分配内存空间,并在方法执行结束后自动释放这些内存空间。
    • 每个方法在执行的同时都会创建一个栈帧(StackFrame)。解析栈帧:
    1. 局部变量表:是用来存储我们临时8个基本数据类型、对象引用地址、returnAddress类型。(returnAddress中保存的是return后要执行的字节码的指令地址。)
    2. 操作数栈:操作数栈就是用来操作的,例如代码中有个 i = 6*6,他在一开始的时候就会进行操作,读取我们的代码,进行计算后再放入局部变量表中去。
    3. 动态链接:假如我方法中,有个 service.add()方法,要链接到别的方法中去,这就是动态链接,存储链接的地方。
    4. 方法出口:出口是什呢,出口正常的话就是return 不正常的话就是抛出异常。
  4. 本地方法栈(Native Method Stack)
    • 与虚拟机栈类似,本地方法栈也是线程私有的内存区域,但它主要用于支持本地方法(Native Method)的执行。
    • native关键字的方法是看不到的,必须要去oracle官网去下载才可以看的到。本地方法通常是由C或C++编写的,通过JNI(Java Native Interface)调用。
  5. 程序计数器(Program Counter Register)
    • 程序计数器是一个较小的内存空间,用于存储当前线程所执行的字节码的行号指示器。
    • 字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令。
    • 由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储。称之为“线程私有”的内存。程序计数器内存区域是虚拟机中唯一没有规定OutOfMemoryError情况的区域。

在JVM中,堆和方法区是共享区,可以被多个线程访问;而虚拟机栈、本地方法栈和程序计数器则是线程私有的,每个线程都有自己独立的内存空间。这样的设计有助于实现线程之间的数据隔离和并发控制。

三、JVM的关键技术

  1. 即时编译(JIT):为了提高Java程序的执行效率,JVM引入了即时编译技术。JIT编译器可以将热点代码(频繁执行的代码)编译成本地机器代码,从而提高执行速度。
  2. 垃圾收集器:JVM提供了多种垃圾收集器,如Serial、Parallel、CMS和G1等。不同的垃圾收集器适用于不同的应用场景,选择合适的垃圾收集器对于提高JVM性能至关重要。
  3. 热点探测:JVM通过热点探测技术识别出热点代码,以便JIT编译器进行编译优化。热点探测主要基于执行计数器和采样两种方式。
  4. 逃逸分析:逃逸分析是一种确定对象是否会在方法体外被引用的技术。通过逃逸分析,JVM可以优化对象的分配和回收策略,提高内存使用效率。

四、JVM性能优化

JVM(Java Virtual Machine)性能优化是确保Java应用程序高效运行的关键步骤。通过合理的配置和调优,可以显著提高JVM的吞吐量、减少垃圾收集(GC)停顿时间、提高响应速度等。以下是对JVM性能优化的详细描述:

  1. 堆内存设置

    • 设置合适的初始堆大小(-Xms)和最大堆大小(-Xmx),以避免频繁的GC和内存溢出。
    • 根据应用程序的特性和需求,调整新生代(Young Generation)和老年代(Old Generation)的比例。
  2. GC调优

    • 选择合适的垃圾收集器(GC)。不同的GC适用于不同的应用场景,如Parallel GC适用于吞吐量优先的场景,CMS GC适用于响应时间优先的场景。
    • 调整GC的触发条件和参数,如GC线程数、GC暂停时间目标等,以平衡吞吐量和响应时间。
    • 使用GC日志分析工具(如GCViewer、GC Easy等)监控和分析GC行为,以便及时发现问题并进行调优。
  3. JIT(Just-In-Time)编译器调优

    • 调整JIT编译器的参数,如编译阈值、内联策略、代码缓存大小等,以提高编译效率和代码执行效率。
    • 使用热点探测技术识别热点代码,以便JIT编译器进行针对性的优化。
  4. 代码优化

    • 编写高效、简洁的代码,避免不必要的内存分配和对象创建。
    • 使用合适的数据结构和算法来降低时间复杂度和空间复杂度。
    • 使用字符串连接池来重用字符串对象,减少内存分配和垃圾收集的开销。
  5. 线程调优

    • 调整线程池的大小和类型,以适应应用程序的并发需求。
    • 设置线程的优先级,以确保关键任务能够得到及时处理。
    • 使用线程同步和并发控制机制(如锁、信号量、CyclicBarrier等)来避免竞态条件和死锁等问题。
  6. 锁优化

    • 减少锁的持有时间,避免长时间持有锁导致其他线程阻塞。
    • 使用轻量级锁和偏向锁等高级锁技术来减少锁的竞争和开销。
    • 使用读写锁(ReadWriteLock)来允许多个线程同时读取共享资源,提高并发性能。
  7. 类加载优化

    • 优化类的加载过程,减少类的加载时间和内存占用。
    • 使用自定义类加载器来加载和管理特定类型的类。
  8. I/O优化

    • 使用NIO(New I/O)或AIO(Asynchronous I/O)来提高I/O操作的性能和效率。
    • 合理地使用缓冲区和缓存来减少I/O操作的次数和开销。
  9. 监控和诊断工具

    • 使用JVM监控和诊断工具(如JConsole、JVisualVM、JProfiler等)来监控和分析JVM的运行状态和性能瓶颈。
    • 根据监控结果调整JVM参数和代码实现,以提高性能。

总之,JVM性能优化是一个综合性的任务,需要从多个方面入手进行调优。通过合理的内存管理、并发性能优化、代码优化和监控诊断等手段,可以显著提高Java应用程序的性能和稳定性。

五、虚拟机类加载机制

  1. 类加载的机制及过程
    程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化。
    类加载的过程
    1、加载
    加载指的是将类的class文件读入到内存,并将这些静态数据转换成方法区中的运行时数据结构,并在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口,这个过程需要类加载器参与。Java类加载器由JVM提供,是所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。类加载器,可以从不同来源加载类的二进制数据,比如:本地Class文件、Jar包Class文件、网络Class文件等等等。类加载的最终产物就是位于堆中的Class对象(注意不是目标类对象),该对象封装了类在方法区中的数据结构,并且向用户提供了访问方法区数据结构的接口,即Java反射的接口。

2、链接过程
当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中(意思就是将java类的二进制代码合并到JVM的运行状态之中)。类连接又可分为如下3个阶段。

  1. 验证:确保加载的类信息符合JVM规范,没有安全方面的问题。主要验证是否符合Class文件格式规范,并且是否能被当前的虚拟机加载处理。
  2. 准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。
  3. 解析:虚拟机常量池的符号引用替换为字节引用过程。

3、初始化
初始化阶段是执行类构造器 () 方法的过程。类构造器 ()方法是由编译器自动收藏类中的 所有类变量的赋值动作和静态语句块(static块)中的语句合并产生,代码从上往下执行。当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化虚拟机会保证一个类的 () 方法在多线程环境中被正确加锁和同步。

  1. 什么是类加载器,类加载器有哪些?
    类加载器
    实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。主要有一下四种类加载器:
  • 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。
  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通ClassLoader.getSystemClassLoader()来获取它。
  • 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。
  1. 什么是双亲委派模型?
    如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。

总结

JVM作为Java平台的核心组件,对于Java程序的性能和稳定性具有重要影响。了解JVM的基本结构、内存模型、关键技术以及性能优化等方面的知识,有助于我们更好地编写高效、稳定的Java程序。

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

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

相关文章

【练习2】

1.汽水瓶 ps:注意涉及多个输入&#xff0c;我就说怎么老不对&#xff0c;无语~ #include <cmath> #include <iostream> using namespace std;int main() {int n;int num,flag,kp,temp;while (cin>>n) {flag1;num0;temp0;kpn;while (flag1) {if(kp<2){if(…

初识C++ · 类和对象(下)

目录 1 再谈构造函数 2 类中的隐式类型转换 3 Static成员 4 友元和内部类 5 匿名对象 6 编译器的一些优化 1 再谈构造函数 先看一段代码&#xff1a; class Date { public :Date(int year, int month, int day){_year year;_month month;_day day;} private:int _ye…

Redis线程模型

文章目录 &#x1f496; Redis 单线程模型⭐ 单线程监听大量的客户端连接⭐ Redis 6.0 之前为什么不用多线程&#xff1f; &#x1f496; Redis多线程⭐ Redis 后台线程⭐ Redis 网络IO多线程 对于读写命令来说&#xff0c;Redis 一直是单线程模型。不过&#xff0c;在 Redis 4…

TinyXML-2介绍

1.简介 TinyXML-2 是一个简单、小巧的 C XML 解析库&#xff0c;它是 TinyXML 的一个改进版本&#xff0c;专注于易用性和性能。TinyXML-2 用于读取、修改和创建 XML 文档。它不依赖于外部库&#xff0c;并且可以很容易地集成到项目中。 tinyXML-2 的主要特点包括&#xff1a…

华为:三层交换机与路由器连通上网实验

三层交换机是一种网络交换机&#xff0c;可以实现基于IP地址的高效数据转发和路由功能&#xff0c;通常用于大型企业、数据中心和校园网络等场景。此外&#xff0c;三层交换机还支持多种路由协议&#xff08;如OSPF、BGP等&#xff09;&#xff0c;以实现更为复杂的网络拓扑结构…

automa警惕通过点击元素打开新的标签页,因为你可能会被他蒙蔽!

大家好&#xff0c;我是大胡子&#xff0c;专注于研究RPA实战与解决方案。 我们经常用到automa里面的【点击元素】组件&#xff0c;但要警惕通过点击元素打开新的标签页&#xff0c;例如下面这个场景&#xff0c;点击公众号的图文消息&#xff0c;之后&#xff0c;要自动输入标…

python环境下labelImg图片标注工具的使用

labelimg GitHub地址 python环境下labelImg图片标注工具的使用 1. 写在开头2. 如何使用2.1安装2.2 启动2.2.1 先启动后设置标注的目录2.2.2 指定标注的目录和预设置的标签 2.3 设置自动保存和显示类别。2.4 保存文件类型2.5 [快捷键](https://github.com/HumanSignal/labelImg…

【数据结构】C/C++ 带头双向循环链表保姆级教程(图例详解!!)

目录 一、前言 二、链表的分类 &#x1f95d;单链表 &#x1f95d;双链表 &#x1f95d;循环链表 &#x1f95d;带头双向循环链表 &#x1f34d;头节点&#xff08;哨兵位&#xff09;的作用 ✨定义&#xff1a; ✨作用&#xff1a; &#x1f347;总结 三、带头双向循环链表 …

技术速递|使用 .NET 为 Microsoft AI 构建可扩展网关

作者&#xff1a;Kara Saucerman 排版&#xff1a;Alan Wang Microsoft AI 团队构建了全面的内容、服务、平台和技术&#xff0c;以便消费者在任何设备上、任何地方获取他们想要的信息&#xff0c;并为企业改善客户和员工的体验。我们的团队支持多种体验&#xff0c;包括 Bing、…

通过氧气退火增强β-Ga₂O₃二极管.中国科技大学和河北半导体研究所的研究人员在这一特定领域取得了最新重大进展

上图所示&#xff1a;&#xff08;a&#xff09;增加台面有助于提高β-Ga2O3肖特基势垒二极管的阻断电压&#xff08;b&#xff09;。 氧气退火和自对准台面终端使β-Ga2O3二极管进一步走向商业化。 虽然β-Ga2O3电力电子技术已经取得了长足的进步&#xff0c;但仍然存在挑战&…

.双链表.

题目&#xff1a; 实现一个双链表&#xff0c;双链表初始为空&#xff0c;支持 55 种操作&#xff1a; 在最左侧插入一个数&#xff1b;在最右侧插入一个数&#xff1b;将第 k&#x1d458; 个插入的数删除&#xff1b;在第 k&#x1d458; 个插入的数左侧插入一个数&#xf…

Redis(Redis配置和订阅发布)

文章目录 1.Redis配置1.网络配置1.配置文件位置 /etc/redis.conf2.bind&#xff08;注销支持远程访问&#xff09;1.默认情况bind 127.0.0.1 只能接受本机的访问2.首先编辑配置文件3.进入命令模式输入/bind定位&#xff0c;输入n查找下一个&#xff0c;shift n查找上一个&…

书生·浦语大模型实战营之XTuner多模态训练与测试

书生浦语大模型实战营之XTuner多模态训练与测试 目录 XTuner多模态训练与测试给LLM装上电子眼&#xff1a;多模态LLM原理简介文本单模态文本图像多模态 电子眼&#xff1a;LLaVA方案简介LLaVA训练阶段示意图LLaVA测试阶段示意图 项目实践环境准备XTuner安装概述Pretrain阶段Fi…

NVIDIA_SMI has failed because it couldn’t communicate with the NVIDIA driver

参考&#xff1a;https://www.zhihu.com/question/474222642/answer/3127013936 https://blog.csdn.net/ZhouDevin/article/details/128265656 nvidia-smi查看报错&#xff0c;nvcc正常 1&#xff09;查看nvidia版本 ls /usr/src | grep nvidia nvidia-550.78 2&#xff09;…

无线通信基础

这里写目录标题 通信概述什么是无线通信无线通信电磁波 通信概述 什么是无线通信 无线通信 : 是指利用电磁波信号可以在自由空间中传播的特性进行信息交换的一种通信方式 无线通信的关键技术包括调制技术、解调技术、信道编码技术、信号处理技术、天线技术等。这些技术的不断…

【mobx-入门与思考】

介绍 mobx 是 nodejs生态中的框架&#xff0c; 主要用于做状态管理&#xff0c;可以监控变量状态的变化。 nodejs中除了mobx&#xff0c;还有个redux&#xff0c;也是做状态管理的&#xff0c;都是比较成熟的框架&#xff0c;二者的选择可以参考 【nodejs状态管理: Redux VS M…

太原理工大学Python数据分析原理与应用(课外考题:8~11章)

这部分大概只考10分&#xff0c;且大部分出在选择题&#xff0c;填空最多一两个 (仅供参考) 第十章 (理解概念为主&#xff0c;无需看推导过程) 第十一章

1-1ARM开发环境搭建(GD32)

1:安装MDK最好是5.27以及以上版本&#xff0c;避免后续学习中出现相关错误 2&#xff1a;安装芯片支持包 双击安装即可&#xff0c;也可以是默认路径&#xff0c;也可以自己更改路径 3&#xff1a;安装jlink下载器驱动&#xff08;下载调试器&#xff09; 具体安装步骤如下所示…

Java 线程池 ( Thread Pool )的简单介绍

想象一下&#xff0c;你正指挥着一支超级英雄团队&#xff0c;面对蜂拥而至的敌人&#xff08;任务&#xff09;&#xff0c;不是每次都召唤新英雄&#xff08;创建线程&#xff09;&#xff0c;而是精心调配现有成员&#xff0c;高效应对。这就是Java线程池的魔力&#xff0c;…

重装win11系统后找不到WiFi

由于电脑崩溃重装了系统&#xff0c;win11,装完之后WiFi图标不见了且网络适配器根本没有无线网络选项。 右键电脑》管理》网络适配器。 在刚装好系统时候并没有前两项&#xff0c;查了很多资料&#xff0c;比如 关机14s 重启&#xff0c;还有通过服务配置 WLAN AutoConfig 都…