JVM运行时数据区域-附面试题
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域
有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是
依赖用户线程的启动和结束而建立和销毁。

1. 程序计数器(Program Counter Register)
- 作用:程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。(简化:用来存储指向下一条指令的地址,即将要执行的指令代码)
- 线程私有:由于 Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。
- 无内存溢出情况:程序计数器是唯一一个在《Java 虚拟机规范》中没有规定任何 OutOfMemoryError 情况的区域。
2. Java 虚拟机栈(Java Virtual Machine Stacks)
- 作用:与程序计数器一样,Java 虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是 Java 方法执行的线程内存模型:每个方法被执行的时候,Java 虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
- 局部变量表:存放了编译期可知的各种 Java 虚拟机基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和 returnAddress 类型(指向了一条字节码指令的地址)。
- 异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常;如果 Java 虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出 OutOfMemoryError 异常。
3. 本地方法栈(Native Method Stacks)
- 作用:本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
- 本地方法是使用非 Java 语言(如 C、C++)实现的方法,它们可以直接访问底层操作系统的资源。例如:Java 程序有时需要与操作系统的底层功能进行交互,比如文件操作、网络操作等。由于 Java 本身的跨平台性,有些底层操作无法直接实现,这时就需要借助本地方法。
- 异常情况:和虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出 StackOverflowError 和 OutOfMemoryError 异常。
4. Java 堆(Java Heap)
- 作用:Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。从内存分配的角度来看,线程共享的 Java 堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。不过无论如何划分,都与存放内容无关,无论哪个区域,存储的都仍然是对象实例。
- 垃圾回收的主要区域:Java 堆是垃圾收集器管理的主要区域,因此很多时候也被称作 “GC 堆”。根据垃圾收集算法的不同,Java 堆还可以细分为:新生代和老年代;再细致一点有 Eden 空间、From Survivor 空间、To Survivor 空间等。
- 异常情况:如果在 Java 堆中没有内存完成实例分配,并且堆也无法再扩展时,Java 虚拟机将会抛出 OutOfMemoryError 异常。
5. 方法区(Method Area)
- 作用:方法区是存放基础信息的位置,线程共享。主要包含三部分内容:1.类的元信息,保存了所有类的基本信息,2.运行时常量池,保存了字节码文件中的常量池内容、3.字符串常量池,保存了字符串常量
- 运行时常量池:是方法区的一部分,Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。运行时常量池相对于 Class 文件常量池的另外一个重要特征是具备动态性,Java 语言并不要求常量一定只有编译期才能产生,也就是说,并非预置入 Class 文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是 String 类的 intern () 方法。
- 异常情况:当方法区无法满足新的内存分配需求时,将抛出 OutOfMemoryError 异常。
- 方法区的实现
-
- JDK7及之前的版本将方法区存放在堆区域中的永久代空间,堆的大小由虚拟机参数来控制。
- JDK8及之后的版本将方法区存放在元空间中,元空间位于操作系统维护的直接内存中,默认情况下只要不超过操作系统承受的上限,可以一直分配。
6. 直接内存(Direct Memory)
- 作用:直接内存并不是虚拟机运行时数据区的一部分,也不是《Java 虚拟机规范》中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现。在 JDK 1.4 中新加入了 NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里面的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。
- 异常情况:由于直接内存不受 Java 堆大小的限制,但是受本机总内存(包括 RAM 及 SWAP 区或者分页文件)大小以及处理器寻址空间的限制,所以在动态分配直接内存时可能会出现 OutOfMemoryError 异常。
面试题
基础概念类
- 问题:请简要介绍一下 JVM 运行时数据区域包含哪些部分?
参考答案:JVM 运行时数据区域主要包含以下几个部分:
-
- 程序计数器:线程私有,可看作当前线程所执行的字节码的行号指示器,是唯一不会出现 OutOfMemoryError 的区域。
- Java 虚拟机栈:线程私有,描述 Java 方法执行的线程内存模型,每个方法执行会创建栈帧,存储局部变量表、操作数栈等信息,可能抛出 StackOverflowError 和 OutOfMemoryError 异常。
- 本地方法栈:与虚拟机栈类似,为虚拟机使用的本地方法服务,也可能抛出 StackOverflowError 和 OutOfMemoryError 异常。
- Java 堆:线程共享,是对象实例分配内存的主要区域,也是垃圾收集器管理的主要区域,可能抛出 OutOfMemoryError 异常。
- 方法区:线程共享,用于存储已被虚拟机加载的类型信息、常量、静态变量等数据,可能抛出 OutOfMemoryError 异常。
- 运行时常量池:是方法区的一部分,存放编译期生成的字面量与符号引用,具备动态性。
- 直接内存:不是虚拟机运行时数据区的一部分,但也会被频繁使用,可能导致 OutOfMemoryError 异常。
- 问题:哪些区域是线程共享的,哪些是线程私有的?
参考答案:线程共享的区域有 Java 堆、方法区(包含运行时常量池);线程私有的区域有程序计数器、Java 虚拟机栈、本地方法栈。
原理机制类
- 问题:Java 虚拟机栈中栈帧的作用是什么,包含哪些内容?
参考答案:栈帧是 Java 虚拟机栈中用于支持方法调用和方法执行的数据结构。每一个方法从调用开始到执行完成的过程,都对应着一个栈帧在虚拟机栈中入栈到出栈的过程。栈帧包含以下内容:
-
- 局部变量表:用于存储方法参数和方法内部定义的局部变量。
- 操作数栈:在方法执行过程中,用于存储中间计算结果和临时数据。
- 动态连接:将符号引用转换为直接引用,实现方法的动态绑定。
- 方法出口:记录方法执行完毕后,从哪个位置继续执行调用该方法的后续代码。
- 问题:运行时常量池的动态性体现在哪里?
参考答案:运行时常量池的动态性体现在它并不局限于编译期生成的常量。Java 语言允许在运行期间将新的常量放入池中,例如 String 类的 intern () 方法。当调用 intern () 方法时,如果运行时常量池中已经包含一个等于此 String 对象的字符串,则返回常量池中的字符串;否则,将此 String 对象添加到常量池中,并返回该 String 对象的引用。
异常处理类
- 问题:在 JVM 运行时数据区域中,哪些区域可能会抛出 OutOfMemoryError 异常?
参考答案:可能抛出 OutOfMemoryError 异常的区域有 Java 堆、方法区、Java 虚拟机栈(当栈容量可以动态扩展时)、本地方法栈(当栈容量可以动态扩展时)和直接内存。例如,当 Java 堆中没有足够的内存来分配新的对象实例,并且堆也无法再扩展时,会抛出 OutOfMemoryError 异常;方法区无法满足新的内存分配需求时,也会抛出该异常。 - 问题:StackOverflowError 和 OutOfMemoryError 有什么区别?
参考答案:StackOverflowError 通常是由于线程请求的栈深度大于虚拟机所允许的深度而抛出的异常,一般是在递归调用方法时没有正确的终止条件,导致栈帧不断入栈,最终栈空间耗尽。而 OutOfMemoryError 是在无法申请到足够的内存时抛出的异常,它可能发生在 Java 堆、方法区、虚拟机栈(动态扩展时)、本地方法栈(动态扩展时)和直接内存等区域。
应用场景类
- 问题:在实际开发中,如何优化 JVM 运行时数据区域的使用?
参考答案:可以从以下几个方面进行优化:
-
- 对于 Java 堆:合理设置堆的大小,避免堆空间过大导致内存浪费或过小导致频繁的垃圾回收。可以根据应用程序的特点,调整新生代和老年代的比例。
- 对于方法区:避免加载过多不必要的类,及时卸载不再使用的类。可以通过设置合适的方法区大小,避免方法区内存溢出。
- 对于 Java 虚拟机栈:合理控制方法调用的深度,避免递归调用过深导致 StackOverflowError。可以适当调整栈的大小。
- 对于直接内存:合理使用 DirectByteBuffer,避免过度分配直接内存。在使用完后,及时释放直接内存资源。
- 问题:请举例说明 JVM 运行时数据区域在多线程环境下的应用和可能遇到的问题。
参考答案:在多线程环境下,每个线程都有自己独立的程序计数器、Java 虚拟机栈和本地方法栈,这些线程私有区域保证了线程之间的独立性。例如,多个线程同时执行不同的方法时,每个线程的栈帧操作互不干扰。而 Java 堆和方法区是线程共享的,多个线程可能会同时访问和修改堆中的对象和方法区中的类信息。可能遇到的问题包括:
-
- 线程安全问题:多个线程同时访问和修改堆中的对象时,可能会导致数据不一致的问题,需要使用同步机制来保证线程安全。
- 内存泄漏问题:如果线程持有对堆中对象的引用,而这些对象不再使用,但线程没有及时释放这些引用,可能会导致内存泄漏。
- 竞争问题:多个线程同时竞争方法区中的类加载锁时,可能会导致性能下降。
相关文章:
JVM运行时数据区域-附面试题
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域 有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是 依赖用户线程的启动和结束而建立和销毁。 1. 程序计…...
Java线程池与Future_优化并发任务执行
1. 引言 1.1 并发编程的重要性 并发编程是现代软件开发中的关键部分,特别是在处理高并发、大数据和分布式系统时。通过并发编程,可以充分利用多核处理器的计算能力,提高系统的吞吐量和响应速度。 1.2 线程池与Future的作用 线程池:提供了对线程资源的有效管理和复用,减…...
HTML(快速入门)
欢迎大家来到我的博客~欢迎大家对我的博客提出指导,有错误的地方会改进的哦~点击这里了解更多内容 目录 一、前言二、HTML基础2.1 什么是HTML?2.2 认识HTML标签2.2.1 HTML标签当中的基本结构2.2.2 标签层次结构 2.3 HTML常见标签2.3.1 标题标签2.3.2 段落标签2.3.3…...
《苍穹外卖》项目学习记录-Day10订单状态定时处理
利用Cron表达式生成器生成Cron表达式 1.处理超时订单 查询订单表把超时的订单查询出来,也就是订单的状态为待付款,下单的时间已经超过了15分钟。 //select * from orders where status ? and order_time < (当前时间 - 15分钟) 遍历集合把数据库…...
WebForms SortedList 深度解析
WebForms SortedList 深度解析 引言 在Web开发领域,对于数据结构的理解与应用至关重要。其中,SortedList类在WebForms中是一个常用的数据结构,它能够帮助开发者高效地管理有序数据集合。本文将深入解析SortedList类在WebForms中的应用,包括其基本概念、常用方法、性能特点…...
AJAX综合案例——图书管理
黑马程序员视频地址: AJAX-Day02-10.案例_图书管理AJAX-Day02-10.案例_图书管理_总结_V1.0是黑马程序员前端AJAX入门到实战全套教程,包含学前端框架必会的(ajaxnode.jswebpackgit),一套全覆盖的第25集视频,…...
如何在Windows、Linux和macOS上安装Rust并完成Hello World
如何在Windows、Linux和macOS上安装Rust并完成Hello World 如果你刚刚开始学习Rust,第一步就是安装Rust并运行你的第一个程序!本文将详细介绍如何在Windows、Linux和macOS上安装Rust,并编写一个简单的“Hello, World!”程序。 1. 安装Rust …...
使用 Redis Streams 实现高性能消息队列
1. 引言 在后端开发中,消息队列是一个常见的组件,主要用于解耦系统、提高吞吐量以及实现异步处理。常见的消息队列包括 Kafka、RabbitMQ 以及 ActiveMQ,但 Redis Streams 作为 Redis 5.0 引入的新特性,也提供了一种高效、轻量的消…...
30.Word:设计并制作新年贺卡以及标签【30】
目录 NO1.2 NO3邮件合并-信函 NO4邮件合并-标签 NO1.2 另存为/F12:考生文件夹:Word.docx布局→页面设置对话框→页边距:上下左右→纸张:宽度/高度(先调页边距🆗)设计→页面颜色→填充效果→…...
Nginx开发01:基础配置
一、下载和启动 1.下载、使用命令行启动:Web开发:web服务器-Nginx的基础介绍(含AI文稿)_nginx作为web服务器,可以承担哪些基本任务-CSDN博客 注意:我配置的端口是81 2.测试连接是否正常 访问Welcome to nginx! 如果…...
数据分析系列--⑨RapidMiner训练集、测试集、验证集划分
一、数据集获取 二、划分数据集 1.导入和加载数据 2.数据集划分 2.1 划分说明 2.2 方法一 2.3 方法二 一、数据集获取 点击下载数据集 此数据集包含538312条数据. 二、划分数据集 1.导入和加载数据 2.数据集划分 2.1 划分说明 2.2 方法一 使用Filter Example Range算子. …...
C基础寒假练习(6)
一、终端输入行数,打印倒金字塔 #include <stdio.h> int main() {int rows;printf("请输入倒金字塔的行数: ");scanf("%d", &rows);for (int i rows; i > 0; i--) {// 打印空格for (int j 0; j < rows - i; j) {printf(&qu…...
mysqldump+-binlog增量备份
注意:二进制文件删除必须使用help purge 不可用rm -f 会崩 一、概念 增量备份:仅备份上次备份以后变化的数据 差异备份:仅备份上次完全备份以后变化的数据 完全备份:顾名思义,将数据完全备份 其中,…...
《DeepSeek R1:大模型最简安装秘籍》
DeepSeek R1:AI 大模型界的新起之秀 在人工智能的璀璨星空中,大模型如繁星般闪耀,而 DeepSeek R1 无疑是其中一颗冉冉升起的新星,自问世以来便吸引了全球的目光,在人工智能领域占据了重要的一席之地。 从性能表现上看…...
FLTK - FLTK1.4.1 - demo - bitmap
文章目录 FLTK - FLTK1.4.1 - demo - bitmap概述笔记END FLTK - FLTK1.4.1 - demo - bitmap 概述 // 功能 : 演示位图数据在按钮上的显示 // * 以按钮为范围或者以窗口为范围移动 // * 上下左右, 文字和图像的相对位置 // 失能按钮,使能按钮 // 知识点 // FLTK可…...
数据库优化:提升性能的关键策略
1. 引言 在后端开发中,数据库的性能直接影响系统的稳定性和响应速度。随着业务增长,数据库查询变慢、负载过高等问题可能会影响用户体验。 本文将介绍数据库优化的关键策略,包括索引优化、查询优化、分库分表、缓存机制等,并结合…...
【Leetcode 每日一题】119. 杨辉三角 II
问题背景 给定一个非负索引 r o w I n d e x rowIndex rowIndex,返回「杨辉三角」的第 r o w I n d e x rowIndex rowIndex 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 数据约束 0 ≤ r o w I n d e x ≤ 33 0 \le rowIndex \le 33 …...
Java小白入门教程:HashSet
目录 一、定义 二、作用 1、存储唯一元素 2、快速查找 3、去除重复 三、使用场景 1、当你需要存储一系列唯一的元素,并且不关心元素的顺序时。 2、当你需要快速判断一个元素是否存在于集合中时。 四、语法及示例 1、创建HashSet 2、添加元素 3、检查元素…...
玩转大语言模型——使用langchain和Ollama本地部署大语言模型
系列文章目录 玩转大语言模型——使用langchain和Ollama本地部署大语言模型 玩转大语言模型——ollama导入huggingface下载的模型 玩转大语言模型——langchain调用ollama视觉多模态语言模型 玩转大语言模型——使用GraphRAGOllama构建知识图谱 玩转大语言模型——完美解决Gra…...
抖♬♬__ac_signature 算法逆向分析
和网页端一样,算法没有问题...
网络编程套接字(中)
文章目录 🍏简单的TCP网络程序服务端创建套接字服务端绑定服务端监听服务端获取连接服务端处理请求客户端创建套接字客户端连接服务器客户端发起请求服务器测试单执行流服务器的弊端 🍐多进程版的TCP网络程序捕捉SIGCHLD信号让孙子进程提供服务 …...
CodeForces 611:New Year and Domino ← 二维前缀和
【题目来源】 https://codeforces.com/contest/611/problem/C 【题目描述】 They say "years are like dominoes, tumbling one after the other". But would a year fit into a grid? I dont think so. Limak is a little polar bear who loves to play. He has r…...
十分钟快速上手 markdown
前言 本人利用寒假期间,将自己所学的markdown的知识,以及将自己常用的一些操作和注意事项记录下来,希望能够帮助大家 一、markdown是什么 Markdown 是一种轻量级标记语言,说白了就是可以让你利用最简单的语法达到最好的排版效果…...
Go语言中的Select
Select 在 Go 语言中,select 是一种用于处理多个通道操作的控制结构。它允许你同时监听多个通道上的通信操作(发送或接收),并根据哪个操作先完成来执行相应的代码块。select 是 Go 并发编程中的一个重要工具,常用于实…...
Vue.js组件开发-实现全屏图片文字缩放切换特效
使用 Vue 实现全屏图片文字缩放切换特效 步骤 创建 Vue 项目:使用 Vue CLI 来快速创建一个新的 Vue 项目。设计组件结构:创建一个包含图片和文字的组件,并实现缩放和切换效果。实现样式:使用 CSS 来实现全屏显示、缩放和切换动画…...
360嵌入式开发面试题及参考答案
解释一下 802.11ax 和 802.11ac/n 有什么区别 速度与带宽 802.11n 支持的最高理论速率为 600Mbps,802.11ac 进一步提升,单流最高可达 866.7Mbps,多流情况下能达到更高,如 1.3Gbps 等。而 802.11ax(Wi-Fi 6)引入了更多先进技术,理论最高速率可达 9.6Gbps,相比前两者有大…...
python recv的概念和使用案例
recv 是网络编程中用于从套接字接收数据的核心函数,常见于 TCP/UDP 通信。以下是其概念、用法和案例详解: 概念 作用:从已连接(TCP)或已绑定(UDP)的套接字接收数据。参数: bufsize:…...
白话DeepSeek-R1论文(三)| DeepSeek-R1蒸馏技术:让小模型“继承”大模型的推理超能力
最近有不少朋友来询问Deepseek的核心技术,陆续针对DeepSeek-R1论文中的核心内容进行解读,并且用大家都能听懂的方式来解读。这是第三篇趣味解读。 DeepSeek-R1蒸馏技术:让小模型“继承”大模型的推理超能力 当大模型成为“老师”,…...
Web3.js详解
Web1&Web2&Web3 以下是Web1、Web2和Web3的详细介绍,以及一个对比表格: Web1 定义:Web1指的是有着固定内容的非许可的开源网络。特点:在Web1时代,网站内容主要由网站管理员或创建者提供,用户只能…...
jvm - GC篇
如何减慢一个对象进入老年代的速度,如何降低GC的次数 堆内存细分 年轻代(Young Generation): 新创建的对象首先被分配在年轻代中。年轻代又被进一步划分为一个Eden区和两个Survivor区(通常称为S0和S1)。…...
