当前位置: 首页 > article >正文

【JVM 深入了解】JVM 到底包含什么?

👉博主介绍: 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO 专家博主

⛪️ 个人社区:个人社区
💞 个人主页:个人主页
🙉 专栏地址: ✅ Java 中级
🙉八股文专题:剑指大厂,手撕 Java 八股文

在这里插入图片描述

文章目录

      • 1. JVM 架构
      • 2. 垃圾收集机制
      • 3. 性能优化
      • 4. 内存模型
      • 5. 类加载过程
      • 6. 字节码执行
      • 7. 常见问题与调试
      • 8. 最佳实践

Java 虚拟机(JVM, Java Virtual Machine)是 Java 平台的核心组件,它负责执行 Java 字节码。JVM 提供了一个跨平台的运行环境,使得 Java 程序可以在不同的操作系统上运行而无需重新编译。以下是关于 JVM 的一些关键内容:

1. JVM 架构

JVM 主要由以下几个部分组成:

  • 类加载器子系统(Class Loader Subsystem)

    • 引导类加载器(Bootstrap Class Loader):加载核心 Java 类库(如 java.lang.*)。
    • 扩展类加载器(Extension Class Loader):加载 Java 扩展库(位于 jre/lib/ext 目录下的类库)。
    • 应用程序类加载器(Application Class Loader):加载用户自定义的应用程序类。
  • 运行时数据区(Runtime Data Area)

    • 方法区(Method Area):存储类的结构信息,如运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容等。
    • 堆(Heap):存储对象实例和数组。堆是所有线程共享的内存区域。
    • 虚拟机栈(VM Stack):每个线程都有一个私有的虚拟机栈,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
    • 本地方法栈(Native Method Stack):与虚拟机栈类似,但为本地方法服务。
    • 程序计数器(Program Counter Register):记录当前线程所执行的字节码指令地址。
  • 执行引擎(Execution Engine)

    • 解释器(Interpreter):逐条解释并执行字节码。
    • 即时编译器(Just-In-Time Compiler, JIT):将热点代码编译成本地机器码,提高执行效率。
    • 垃圾收集器(Garbage Collector, GC):自动管理内存,回收不再使用的对象。
    • 本地接口(Native Interface):与本地方法库交互,调用本地方法。
  • 本地方法库(Native Method Libraries)

    • 包含了特定平台相关的本地方法实现,如文件 I/O、网络通信等。

2. 垃圾收集机制

JVM 的垃圾收集机制主要负责自动管理内存,回收不再使用的对象。常见的垃圾收集算法包括:

  • 标记-清除(Mark and Sweep)

    • 标记阶段:从根集合开始遍历,标记所有可达的对象。
    • 清除阶段:回收未被标记的对象。
  • 复制(Copying)

    • 将存活对象从一个空间复制到另一个空间,然后清空原来的内存空间。
  • 标记-整理(Mark and Compact)

    • 标记阶段:标记所有可达的对象。
    • 整理阶段:将存活对象向一端移动,然后清理边界外的内存。
  • 分代收集(Generational Collection)

    • 将堆分为新生代(Young Generation)和老年代(Old Generation),针对不同代采用不同的收集策略。
    • 新生代通常使用复制算法,老年代通常使用标记-整理或标记-清除算法。

3. 性能优化

  • JIT 编译器

    • 通过将热点代码编译成本地机器码,提高执行效率。
    • 可以通过 -XX:CompileThreshold 参数调整触发 JIT 编译的阈值。
  • 垃圾收集器选择

    • 不同的垃圾收集器适用于不同的场景,如 Serial GC、Parallel GC、CMS GC 和 G1 GC。
    • 可以通过 -XX:+UseSerialGC-XX:+UseParallelGC-XX:+UseConcMarkSweepGC-XX:+UseG1GC 等参数选择合适的垃圾收集器。
  • 堆大小设置

    • 通过 -Xms-Xmx 参数设置初始堆大小和最大堆大小。
    • 合理设置堆大小可以避免频繁的垃圾收集和 OutOfMemoryError。
  • 其他优化参数

    • -XX:NewRatio:设置新生代和老年代的比例。
    • -XX:SurvivorRatio:设置 Eden 区和 Survivor 区的比例。
    • -XX:MaxTenuringThreshold:设置晋升老年代的最大年龄。
    • -XX:ParallelGCThreads:设置并行垃圾收集的线程数。

4. 内存模型

JVM 的内存模型规定了多线程环境下对共享变量的访问规则,主要包括:

  • 主内存与工作内存

    • 每个线程都有自己的工作内存,存储了该线程读写共享变量的副本。
    • 主内存中存储了所有的共享变量。
  • 原子性、可见性和有序性

    • 原子性:保证基本数据类型的读写操作是原子的。
    • 可见性:确保一个线程对共享变量的修改对其他线程是可见的。
    • 有序性:禁止编译器和处理器对指令进行重排序。
  • volatile 关键字

    • 保证变量的可见性和有序性。
    • 防止指令重排序。
  • synchronized 关键字

    • 保证代码块的原子性和可见性。
    • 通过监视器锁(Monitor Lock)实现互斥访问。

5. 类加载过程

类加载过程主要包括以下步骤:

  • 加载(Loading)

    • 通过类的全限定名获取二进制流。
    • 将二进制流转换为方法区内的运行时数据结构。
    • 在内存中生成一个代表这个类的 java.lang.Class 对象。
  • 验证(Verification)

    • 确保被加载的类符合 JVM 规范,防止恶意代码破坏 JVM 安全。
  • 准备(Preparation)

    • 为类的静态变量分配内存,并设置默认初始值。
  • 解析(Resolution)

    • 将符号引用替换为直接引用。
  • 初始化(Initialization)

    • 执行类构造器 <clinit>() 方法,初始化静态变量和静态代码块。

6. 字节码执行

  • 字节码

    • Java 源代码经过编译后生成的中间表示形式。
    • 字节码是一种平台无关的二进制格式。
  • 解释执行

    • 解释器逐条解释并执行字节码。
  • 即时编译

    • JIT 编译器将热点代码编译成本地机器码,提高执行效率。

7. 常见问题与调试

  • OutOfMemoryError

    • 堆内存溢出(Heap Space):增加堆内存大小。
    • 栈内存溢出(Stack Overflow):检查递归调用深度。
    • 方法区内存溢出(PermGen 或 Metaspace):增加方法区大小。
  • 性能监控工具

    • JConsole:图形化监控工具,可以查看 JVM 的内存使用情况、线程状态等。
    • VisualVM:更强大的图形化监控工具,支持性能分析、内存快照等功能。
    • jstat:命令行工具,用于监控 JVM 的性能统计信息。
    • jmap:命令行工具,用于生成堆转储快照。
    • jstack:命令行工具,用于打印线程堆栈跟踪信息。

8. 最佳实践

  • 合理设置堆大小

    • 根据应用的实际需求设置 -Xms-Xmx,避免频繁的垃圾收集。
  • 选择合适的垃圾收集器

    • 根据应用的特点选择适合的垃圾收集器,如低延迟应用可以选择 G1 GC。
  • 减少不必要的对象创建

    • 使用对象池、缓存等技术减少对象的创建和销毁。
  • 使用并发编程

    • 利用多线程提高应用的并发处理能力,但要注意线程安全问题。
  • 定期进行性能分析

    • 使用性能监控工具定期分析应用的性能瓶颈,进行针对性优化。

JVM 是 Java 应用程序运行的基础,理解 JVM 的架构、垃圾收集机制、性能优化、内存模型、类加载过程等内容对于开发高性能、可靠的 Java 应用至关重要。通过合理的配置和最佳实践,可以显著提升应用的性能和稳定性。

精彩专栏推荐订阅:在下方专栏👇🏻
✅ 2023年华为OD机试真题(A卷&B卷)+ 面试指导
✅ 精选100套 Java 项目案例
✅ 面试需要避开的坑(活动)
✅ 你找不到的核心代码
✅ 带你手撕 Spring
✅ Java 初阶

在这里插入图片描述


http://www.kler.cn/a/373371.html

相关文章:

  • Spring自动装配(特别版)
  • 2024-10-30 学习人工智能的Day18
  • uniapp实现【时间戳转换为日期格式(年-月-日 时-分-秒)】
  • 活动|2024 CodeFuse 「编码挑战季」活动已开启!欢迎报名参加
  • 书生大模型实战营 L0 入门岛
  • 链表:两数相加
  • Pycharm报错:Error:failed to find libmagic. Check your installation
  • 信息学奥赛复赛复习19-CSP-J2023-02公路-贪心算法、向上取整、向下取整
  • C# 支持三种方式实现创建 XML文档
  • 关于Android Studio Koala Feature Drop | 2024.1.2下载不了插件的解决办法
  • PHP反序列化-pikachu
  • JavaEE 多线程第四节 (线程核心操作----线程开始/线程终止)
  • 【机器学习】线性回归模型
  • Linux系统rpm安装MySQL详细操作步骤
  • 19 Docker容器集群网络架构:二、etcd 集群部署
  • 【Java多线程】8 Java 中的并发设计模式
  • 【K8S系列】Kubernetes 中 NodePort 类型的 Service 无法访问的问题【已解决】
  • MySQL(2)【库的操作】
  • python爬虫案例——使用aiohttp模块异步请求网站,利用协程加快爬取速度(17)
  • 数据可视化工具深入学习:Seaborn 与 Plotly 的详细教程
  • Linux驱动开发(1):环境搭建
  • 工厂方法模式与抽象工厂模式
  • 九泰智库 | 医械周刊- Vol.65 | 广州发布首批创新药械产品目录
  • libavdevice.so.58: cannot open shared object file: No such file ordirectory踩坑
  • XXE漏洞原理、修复建议及绕过方式
  • 蓝牙4.0/5.1/5.2模组UART通讯基础知识