您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

面试必问,JVM内存模型扫盲

时间:2023-06-07 14:00:36  来源:微信公众号  作者:一灯架构
运行时常量池(Runtime Constant Pool)是方法区中的一部分,用于存储编译期间生成的各种字面量和符号引用。在JAVA程序运行时,JVM将编译期生成的class文件中的常量池内容读取到运行时常量池中。

JVM简介

JVM(Java Virtual machine,Java虚拟机)是Java语言的核心,是一个用于解释Java字节码的虚拟计算机。它可以在运行Java程序时自动管理内存、处理异常等。Java程序员不需要关心底层硬件和操作系统的细节,只需要编写符合Java语法规范的代码,就可以实现跨平台的编程

当我们编写Java程序时,Java源代码会被编译成为Java字节码( .java 文件被编译成 .class 文件)。这些字节码可以在任何安装了Java虚拟机的平台上运行。JVM在执行Java字节码时,将其转换成特定于底层CPU和操作系统的机器代码。

运行时数据区简介

为了执行字节码,JVM在内存中定义了一系列的数据区,用于在运行时存储各类数据,即运行时数据区(Runtime Data Areas)。理解这些数据区及其作用,是掌握Java性能调优和错误排查的关键。

JVM 运行时数据区是 Java 虚拟机在执行 Java 程序时用于数据存储的内存区域,这些区域各司其职,确保了 Java 程序的正确执行。JVM 运行时数据区主要分为五个部分:程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、堆(Heap)、方法区(Method Area)。JVM运行时数据区在程序运行时动态地分配和释放内存,内存管理由JVM自动完成。不同的数据区域有不同的内存管理机制和垃圾回收算法,以保证程序运行的效率和稳定性。

其中程序计数器、虚拟机栈、本地方法栈属于线程私有区域,跟随线程的启动和结束而建立和销毁。堆和方法区是线程共享区域,跟随虚拟机进程的启动而存在。

程序计数器(Program Counter Register) 是一块较小的内存空间,作用是指示当前线程正在执行的 JVM 字节码指令地址。

虚拟机栈(VM Stack) 存放的是一些基本类型的变量(如int, long)和对象引用。Java 方法执行的内存模型是以栈帧(Stack Frame)为基础的,每个方法在执行的时候都会创建一个栈帧,栈帧中存放了局部变量表、操作数栈、动态链接、方法出口等信息。

本地方法栈(Native Method Stack) 与虚拟机栈类似,其主要服务于 JVM 使用到的 Native 方法。

堆区(Heap) 是 JVM 所管理的最大一块内存空间,主要用于存放所有线程共享的 Java 对象实例。这也是垃圾回收器主要活动区域。

方法区(Method Area) 是用来存储加载的类信息、常量、静态变量等数据的。这个区域是线程共享的。

图片

1. 程序计数器

程序计数器(Program Counter Register)是线程私有区域,生命周期与线程一致,也是 JVM 内存中唯一一个没有任何 OutOfMemoryError 的区域。

程序计数器的作用是记录当前线程正在执行的指令地址,换句话说,它指向了下一条将要被执行的 JVM 字节码指令。在 JVM 的概念模型中,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

当线程执行的是 Java 方法时,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器的值则为空(Undefined)。

程序计数器对于现代多线程而言至关重要,因为在 CPU 切换各个线程时,需要将各个线程的程序计数器记录下来,以便在下一次切换回这个线程时,能知道该从哪里继续执行。

总结:

  • 程序计数器是一块很小的内存空间,也是运行速度最快的存储区域。
  • 在 JVM 规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期一致。
  • 如果当前线程正在执行的是 Java 方法,程序计数器记录的是 JVM 字节码指令地址,如果是执行 native 方法,则是未指定值(undefined)
  • 它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成
  • 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
  • 它是唯一一个在 JVM 规范中没有规定任何 OutOfMemoryError 情况的区域

2. 虚拟机栈

与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,生命周期与线程相同。描述的是Java方法执行的内存模型。

在 JVM 中,每当一个新的线程被创建,都会创建一个与之关联的私有 JVM 栈。这个栈会随着线程的运行而进行入栈(push)和出栈(pop)操作。它主要用于存储局部变量、操作数堆栈以及方法调用的情况。

JVM 栈是由一系列栈帧(Stack Frame)组成的。每当一个方法被调用,一个新的栈帧就会被压入栈中,每当一个方法调用结束,一个栈帧就会被弹出栈。每个栈帧中都包含了局部变量表、操作数栈、动态链接和方法返回地址等信息。

局部变量表主要存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不等同于指针,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和 returnAddress 类型(指向了一条字节码指令的地址)。

操作数栈则是在执行字节码指令时用到的临时存储区,比如在进行算数运算时,操作数栈就会用来存放操作数和接收结果。

Java虚拟机栈可能会抛出以下异常:

  1. 如果线程请求的栈深度大于 JVM 所允许的深度,将抛出 StackOverflowError。
  2. 如果 JVM 栈可以动态扩展,当扩展时无法申请到足够的内存,会抛出 OutOfMemoryError。

图片

3. 本地方法栈

本地方法栈(Native Method Stack)也是线程私有,生命周期与线程相同。作用是与虚拟机栈类似,虚拟机栈是为Java 方法服务的,而本地方法栈是为 Native 方法服务的。

和虚拟机栈一样,本地方法栈的大小可以是固定的也可以是动态的。如果是固定的,当线程请求的栈深度超过最大深度时,会抛出 StackOverflowError。如果是动态的,并且在尝试扩展时无法申请到足够的内存,会抛出 OutOfMemoryError。

4. 堆

堆(Heap)是 JVM 所管理的最大一块内存空间,也是所有线程共享的一块内存区域,在虚拟机启动时创建。堆主要用于存储对象实例和数组,这也是 Java 垃圾回收器主要活动的区域。

在物理上,堆区可以处于分散的内存空间中,但在逻辑上它被视为连续的。堆区在 JVM 启动时创建,如果堆区的空间不足,将会抛出 OutOfMemoryError。

堆分为新生代(Young Generation)和老年代(Old Generation)。新生代又分为 Eden 区、From Survivor 区(简称 S0)、 To Survivor 区(简称 S1)。划分这么多区域的目的是为了更好地回收内存,或者更快地分配内存。

新生代中各个区域的内存占比分别是,Eden : S0 : S1 = 8 : 1 : 1

新创建的对象优先在  Eden 区进行分配。当 Eden 区满时,会触发一次 Minor GC(新生代垃圾回收,也叫 Young GC),将仍然存活的对象从 Eden 区和 S0 区移动到 S1 区,下次 Minor GC 处理情况类似,把存活的对象从 Eden 区和 S1 区移动到 S0 区。当 Survivor 区也满了,还存活的对象会被移动到老年代。如果老年代也满了,将会触发 Major GC(老年代垃圾回收,也叫 Old GC)。当老年代满了,也可能触发 Full GC,Full GC 会对整个堆内存进行垃圾回收,包含新生代、老年代和方法区。Full GC 会导致较长的停顿时间,并且会消耗大量的系统资源。

图片

5. 方法区

方法区(Method Area)与堆一样,是所有线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

方法区只是 JVM 规范中定义的一个概念,针对 Hotspot 虚拟机,JDK8 之前使用永久代(Permanent Generation,简称 PermGen)实现,JDK8 使用元空间(Metaspace)实现。

JDK8 之前可以通过 -XX:PermSize 和 -XX:MaxPermSize 来设置永久代大小,JDK8 之后,使用元空间替换了永久代,改为通过 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 来设置元空间大小。

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区中的一部分,用于存储编译期间生成的各种字面量和符号引用。在Java程序运行时,JVM将编译期生成的class文件中的常量池内容读取到运行时常量池中。

运行时常量池存储了类和接口中的常量,包括字符串字面量、被声明为final的常量值等。它还存储了类和接口中的符号引用,如类和接口、字段和方法的引用等。

在JVM中,运行时常量池是线程安全的。每个线程都有一个自己的线程栈,其中包含了局部变量表,而这些局部变量表中所引用的对象都位于堆中。当一个线程需要引用运行时常量池中的常量时,JVM会先将常量值从运行时常量池中复制到线程栈的局部变量表中,然后再进行引用。

需要注意的是,在JDK8中,运行时常量池已经被移动到元空间(Metaspace)中。元空间是在本地内存中分配的,与JVM的堆内存是分离的,因此不会受到Java堆大小的限制。



Tags:JVM   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Graalvm 替代 JVM 真的可以带来巨大的性能优势吗?
介绍Spring Boot有助于轻松开发独立的、可用于生产的 Spring 应用程序。它对 Spring 平台和第三方库采用固执己见的方法:以最少的配置简化设置过程。优势: 易于使用:Spring Boo...【详细内容】
2023-12-25  Search: JVM  点击:(127)  评论:(0)  加入收藏
理解Java虚拟机(JVM):优化代码执行效率的内部机制
Java虚拟机(Java Virtual Machine,JVM)是Java程序运行的核心组件,它负责将Java源代码编译成字节码并执行。JVM具有内部机制来优化代码的执行效率,包括即时编译(Just-In-Time Compi...【详细内容】
2023-12-14  Search: JVM  点击:(220)  评论:(0)  加入收藏
深入了解Java的GC原理,掌握JVM 性能调优!
JVM性能调优是一个复杂的过程,需要结合具体的应用程序特性和需求来进行调优。不同的应用场景可能需要不同的调优策略。对于 Java 开发人员来说,进行程序的性能优化是很有挑战...【详细内容】
2023-12-12  Search: JVM  点击:(196)  评论:(0)  加入收藏
GC是什么?为什么要GC?JVM 垃圾回收算法有哪些?
Major GC 老年代区域的垃圾回收,老年代空间不足时,会先尝试触发Minor GC。Minor GC之后空间还不足,则会触发Major GC,Major GC速度比较慢,暂停时间长。图片1 Java垃圾回收机制(GC...【详细内容】
2023-12-07  Search: JVM  点击:(223)  评论:(0)  加入收藏
JVM由那些部分组成,运行流程是什么?
思考: JVM由那些部分组成,运行流程是什么?1.JVM由那些部分组成,运行流程是什么?JVM是什么好处:一次编写,到处运行自动内存管理,垃圾回收机制思考:JVM由哪些部分组成,运行流程是什么?...【详细内容】
2023-12-06  Search: JVM  点击:(205)  评论:(0)  加入收藏
JVM的调优常用参数
调优目的JVM调优的目的是为了提高Java应用程序的性能和稳定性。通过优化JVM的配置和参数设置,可以减少内存占用、提高垃圾回收效率、优化线程管理等,从而提升应用程序的响应速...【详细内容】
2023-11-10  Search: JVM  点击:(201)  评论:(0)  加入收藏
JVM 解释和编译指南
Java 是一种跨平台的编程语言。程序源代码会被编译为 字节码bytecode,然后字节码在运行时被转换为 机器码machine code。解释器interpreter 在物理机器上模拟出的抽象计算机...【详细内容】
2023-11-07  Search: JVM  点击:(366)  评论:(0)  加入收藏
深入理解并发编程艺术之JVM内存模型
java内存模型由来我们知道不同的计算机硬件和操作系统的,所遵循的规范以及计算机内存模型是有区别的,也就意味着我们开发的程序放在某个计算机硬件和操作系统上运行是正常的,而...【详细内容】
2023-10-27  Search: JVM  点击:(431)  评论:(0)  加入收藏
OOM异常会导致JVM退出吗?
熟悉Java开发的人,应该会经常遇到的异常:OOM,那么这个异常会导致 JVM 虚拟机退出吗?结论Java虚拟机(JVM)在运行Java应用时,可能会遇到内存不足的情况,从而抛出OutOfMemoryError(OOM)。...【详细内容】
2023-10-13  Search: JVM  点击:(243)  评论:(0)  加入收藏
JVM是如何判定对象已死的?学JVM必会的知识!
大家好,我是 BookSea。作为一名Java程序员,我们每天都在程序里不停地去new对象,但是你知道这些被new出来的对象,最后是怎么被回收的吗?在堆里面存放着Java世界中几乎所有的对象实...【详细内容】
2023-10-08  Search: JVM  点击:(333)  评论:(0)  加入收藏
▌简易百科推荐
Java 8 内存管理原理解析及内存故障排查实践
本文介绍Java8虚拟机的内存区域划分、内存垃圾回收工作原理解析、虚拟机内存分配配置,以及各垃圾收集器优缺点及场景应用、实践内存故障场景排查诊断,方便读者面临内存故障时...【详细内容】
2024-03-20  vivo互联网技术    Tags:Java 8   点击:(14)  评论:(0)  加入收藏
如何编写高性能的Java代码
作者 | 波哥审校 | 重楼在当今软件开发领域,编写高性能的Java代码是至关重要的。Java作为一种流行的编程语言,拥有强大的生态系统和丰富的工具链,但是要写出性能优异的Java代码...【详细内容】
2024-03-20    51CTO  Tags:Java代码   点击:(24)  评论:(0)  加入收藏
在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述
译者 | 李睿审校 | 重楼在Java开发领域,优化应用程序的性能是开发人员的持续追求。配置文件引导优化(Profile-Guided Optimization,PGO)是一种功能强大的技术,能够显著地提高Ja...【详细内容】
2024-03-18    51CTO  Tags:Java   点击:(25)  评论:(0)  加入收藏
Java生产环境下性能监控与调优详解
堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和 Survivor 区,...【详细内容】
2024-02-04  大雷家吃饭    Tags:Java   点击:(56)  评论:(0)  加入收藏
在项目中如何避免和解决Java内存泄漏问题
在Java中,内存泄漏通常指的是程序中存在一些不再使用的对象或数据结构仍然保持对内存的引用,从而导致这些对象无法被垃圾回收器回收,最终导致内存占用不断增加,进而影响程序的性...【详细内容】
2024-02-01  编程技术汇  今日头条  Tags:Java   点击:(68)  评论:(0)  加入收藏
Java中的缓存技术及其使用场景
Java中的缓存技术是一种优化手段,用于提高应用程序的性能和响应速度。缓存技术通过将计算结果或者经常访问的数据存储在快速访问的存储介质中,以便下次需要时可以更快地获取。...【详细内容】
2024-01-30  编程技术汇    Tags:Java   点击:(72)  评论:(0)  加入收藏
JDK17 与 JDK11 特性差异浅谈
从 JDK11 到 JDK17 ,Java 的发展经历了一系列重要的里程碑。其中最重要的是 JDK17 的发布,这是一个长期支持(LTS)版本,它将获得长期的更新和支持,有助于保持程序的稳定性和可靠性...【详细内容】
2024-01-26  政采云技术  51CTO  Tags:JDK17   点击:(89)  评论:(0)  加入收藏
Java并发编程高阶技术
随着计算机硬件的发展,多核处理器的普及和内存容量的增加,利用多线程实现异步并发成为提升程序性能的重要途径。在Java中,多线程的使用能够更好地发挥硬件资源,提高程序的响应...【详细内容】
2024-01-19  大雷家吃饭    Tags:Java   点击:(106)  评论:(0)  加入收藏
这篇文章彻底让你了解Java与RPA
前段时间更新系统的时候,发现多了一个名为Power Automate的应用,打开了解后发现是一个自动化应用,根据其描述,可以自动执行所有日常任务,说的还是比较夸张,简单用了下,对于office、...【详细内容】
2024-01-17  Java技术指北  微信公众号  Tags:Java   点击:(97)  评论:(0)  加入收藏
Java 在 2023 年仍然流行的 25 个原因
译者 | 刘汪洋审校 | 重楼学习 Java 的过程中,我意识到在 90 年代末 OOP 正值鼎盛时期,Java 作为能够真正实现这些概念的语言显得尤为突出(尽管我此前学过 C++,但相比 Java 影响...【详细内容】
2024-01-10  刘汪洋  51CTO  Tags:Java   点击:(75)  评论:(0)  加入收藏
站内最新
站内热门
站内头条