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

JVM虚拟机

JVM 概述

Java虚拟机(JVM)是运行Java字节码的虚拟计算机,是Java平台的核心组件。它负责将字节码转换为特定操作系统的机器指令,实现“一次编写,到处运行”的特性。JVM包含内存管理、垃圾回收、即时编译(JIT)等机制。

JVM执行流程

程序在执⾏之前先要把java代码转换成字节码(class⽂件),JVM⾸先需要把字节码通过⼀定的⽅式类加载器(ClassLoader)把⽂件加载到内存中运⾏时数据区(RuntimeDataArea),⽽字节码⽂件是JVM的⼀套指令集规范,并不能直接交个底层操作系统去执⾏,因此需要特定的命令解析器**执⾏引擎(ExecutionEngine**将字节码翻译成底层系统指令再交由CPU去执⾏,⽽这个过程中需要调⽤其他语⾔的接⼝本地库接⼝(NativeInterface)来实现整个程序的功能,这就是这4个主要组成部分的职责与功能。

运行时数据区

  • 堆(Heap):存储对象实例和数组,是垃圾回收的主要区域。
  • 方法区(Method Area):存储类元数据、常量池等,JDK 8后由元空间(Metaspace)替代。
  • 虚拟机栈(VM Stack):存储栈帧(局部变量表、操作数栈等),每个线程私有。
  • 本地方法栈(Native Method Stack):支持Native方法调用。
  • 程序计数器(PC Register):记录当前线程执行的字节码指令地址。

1.堆(线程共享)

堆⾥⾯分为两个区域:新⽣代和⽼⽣代,新⽣代放新建的对象,当经过⼀定GC次数之后还存活的对象 会放⼊⽼⽣代。新⽣代还有3个区域:⼀个Endn+两个Survivor(S0/S1)。

垃圾回收的时候会将Endn中存活的对象放到⼀个未使⽤的Survivor中,并把当前的Endn和正在使 ⽤的Survivor清楚掉。

通过-Xms-Xmx参数设置初始和最大堆大小。

2.Java虚拟机栈(线程私有)

Java 虚拟机栈的作⽤:Java虚拟机栈的⽣命周期和线程相同,Java虚拟机栈描述的是Java⽅法执⾏的内存模型:每个⽅法在执⾏的同时都会创建⼀个栈帧(StackFrame)⽤于存储局部变量表、操作数栈、动态链接、⽅法出⼝等信息。咱们常说的堆内存、栈内存中,栈内存指的就是虚拟机栈。

Java 虚拟机栈中包含了以下4部分:

  • 局部变量表:存放了编译器可知的各种基本数据类型(8⼤基本数据类型)、对象引⽤。局部变量表 所需的内存空间在编译期间完成分配,当进⼊⼀个⽅法时,这个⽅法需要在帧中分配多⼤的局部变量空间是完全确定的,在执⾏期间不会改变局部变量表⼤⼩。简单来说就是存放⽅法参数和局部变量。
  • 操作栈:每个⽅法会⽣成⼀个先进后出的操作栈。
  • 动态链接:指向运⾏时常量池的⽅法引⽤。
  • ⽅法返回地址:PC寄存器的地址。

3.本地方法栈(线程私有)

本地⽅法栈和虚拟机栈类似,只不过Java虚拟机栈是给JVM使⽤的,⽽本地⽅法栈是给本地⽅法使 ⽤的。

4.程序计数器(线程私有)

程序计数器是⼀块⽐较⼩的内存空间,如果当前线程正在执⾏的是⼀个Java⽅法,这个计数器记录的是正在执⾏的虚拟机字节码指令的地址;

唯一不会抛出OutOfMemoryError的区域。

5.方法区(线程共享)

⽤来存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据的。

什么是线程私有?

由于JVM的多线程是通过线程轮流切换并分配处理器执⾏时间的⽅式来实现,因此在任何⼀个确定的 时刻,⼀个处理器(多核处理器则指的是⼀个内核)都只会执⾏⼀条线程中的指令。因此为了切换线程后 能恢复到正确的执⾏位置,每条线程都需要独⽴的程序计数器,各条线程之间计数器互不影响,独⽴ 存储。我们就把类似这类区域称之为"线程私有"的内存。

JVM类加载

1.类加载过程

  • 加载阶段 查找并加载类的二进制数据(.class文件),生成一个代表该类的Class对象。类加载器通过类的全限定名获取二进制字节流,将字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  • 验证阶段 确保加载的类信息符合JVM规范,包括文件格式验证、元数据验证、字节码验证和符号引用验证。防止恶意代码或不符合规范的字节码危害JVM运行安全。
  • 准备阶段 为类变量(static变量)分配内存并设置初始值(零值)。例如static int value=123在准备阶段后值为0,初始化阶段才会变为123。若变量被final修饰(如static final int value=123),准备阶段直接赋值为123。
  • 解析阶段 将常量池内的符号引用替换为直接引用。符号引用是一组描述目标的字面量,直接引用可以是直接指向目标的指针或间接定位的句柄。
  • 初始化阶段 执行类构造器<clinit>()方法,包括静态变量赋值和静态代码块。虚拟机会保证子类<clinit>()执行前父类的<clinit>()已执行。若多个线程同时初始化一个类,JVM会同步锁定。

2.双亲委派模型

工作流程:

  • 类加载请求先由当前类加载器委派给父类加载器处理。
  • 父类加载器无法完成时(搜索范围内未找到类),子类加载器才尝试加载。

类加载器分类

  • 启动类加载器(Bootstrap ClassLoader) 由C++实现,负责加载JAVA_HOME/lib目录下的核心类库(如rt.jar)。无法被Java程序直接引用。
  • 应用程序类加载器(Application ClassLoader) 加载用户类路径(ClassPath)的类库。由sun.misc.Launcher$AppClassLoader实现,是默认的类加载器。
  • 自定义类加载器 通过继承ClassLoader类实现,需重写findClass()方法。常见场景包括热部署、代码加密解密等。

优势:

  • 避免重复加载类:⽐如A类和B类都有⼀个⽗类C类,那么当A启动时就会将C类加载起来,那 么在B类进⾏加载时就不需要在重复加载C类了。
  • 安全性:使⽤双亲委派模型也可以保证了Java的核⼼API不被篡改,如果没有使⽤双亲委派模 型,⽽是每个类加载器加载⾃⼰的话就会出现⼀些问题,⽐如我们编写⼀个java.lang.Object 类的话,那么程序运⾏的时候,系统就会出现多个不同的Object类,⽽有些Object类⼜是⽤⼾⾃ ⼰提供的因此安全性就不能得到保证了。

垃圾回收

垃圾回收(Garbage Collection,GC)是自动内存管理的一种机制,主要用于释放程序中不再使用的内存。通过垃圾回收器(Garbage Collector)自动检测并回收无用对象,减少内存泄漏和手动管理内存的复杂性。

垃圾回收的主要算法

标记-清除算法(Mark-Sweep)

  • 标记阶段:从根对象(如全局变量、栈中变量)出发,遍历所有可达对象并标记为“存活”。
  • 清除阶段:扫描整个堆内存,回收未被标记的对象内存。
  • 缺点:产生内存碎片,可能导致大对象分配失败。

复制算法(Copying)

  • 将内存分为两个大小相等的区域(From 和 To),每次只使用其中一个。
  • 回收过程:将存活对象从 From 复制到 To,完成后直接清空 From 区。
  • 优点:避免内存碎片,分配高效。
  • 缺点:内存利用率仅 50%,适合存活对象少的场景(如新生代)。

分代收集算法(Generational)

  • 分代假设:大部分对象生命周期短,少数长期存活。
  • 实现
    • 新生代:使用复制算法(如 HotSpot 的 Eden + Survivor 区)。
    • 老年代:使用标记-清除或标记-整理算法。
  • 优点:针对不同代优化,平衡效率与开销。

引用计数算法(Reference Counting)

  • 每个对象维护一个引用计数器,被引用时加 1,引用失效时减 1。
  • 计数器为 0 时立即回收对象。
  • 优点:实时性高,无需暂停程序。
  • 缺点:无法处理循环引用(如对象 A 引用 B,B 引用 A)。

http://www.dtcms.com/a/346185.html

相关文章:

  • Leetcode—1683. 无效的推文【简单】
  • 网络与信息安全有哪些岗位:(7)等级保护测评师
  • tensorflow-gpu 2.7下的tensorboard与profiler插件版本问题
  • 第九章 Leaflet 实战:多边形绘制工具开发与面积实时计算(含双击报错修复方案)
  • Qt QML实现 无边框圆角窗口拖动(附窗口控制按钮)
  • RAG初筛方案实例验证-多种BM25方案
  • 类器官培养基系列,助力高效医学研究
  • Navicat连接MySQL-出现1045无法连接问题
  • AI实验管理神器:WandB全功能解析
  • 【python】os.mkdir() 和 os.makedirs()区别
  • 数学建模-灰色关联分析
  • map_set
  • Trie 树(字典树)
  • Rust 入门 注释和文档之 cargo doc (二十三)
  • 51单片机-中断系统
  • 【数据分享】各省及全国GDP增长指数(1980-2022)
  • 彻底解决 Windows 文件扩展名隐藏问题,注册表修改显示文件后缀方法
  • More Effective C++ 条款01:仔细区别 pointers 和 references
  • 构建城市数字孪生底座:深度解析智慧城市全景视频拼接融合解决方案
  • constraint_mode使用
  • 【Python】两条命令永久切国内源
  • Android 16环境开发的一些记录
  • C语言中的CSI_START和CSI_END宏
  • 拿到手一个前端项目,应该如何启动
  • 多目标跟踪中基于目标威胁度评估的传感器控制方法复现
  • lanczos算法学习笔记
  • 【GM3568JHF】FPGA+ARM异构开发板 测试命令
  • OFD格式文件及Python将PDF转换为OFD格式文件
  • Informer参数代码
  • SPI的DMA方式