【Java高频面试问题】JVM篇

【Java高频面试问题】JVM篇

  • 类加载机制
    • 加载(Loading)
    • 连接(Linking)‌
    • 初始化(Initialization)‌
    • 使用(Using)与卸载(Unloading)
  • 类加载器和双亲委派模型
    • 类加载器
    • 双亲委派模型
  • 垃圾收集算法
    • 标记-清除算法
    • 复制算法
    • 标记-整理算法
  • 垃圾收集器
    • 串行收集器
      • Serial 收集器
      • Serial Old 收集器
    • 并行收集器
      • ParNew 收集器
      • Parallel Scavenge 收集器
      • Parallel Old 收集器
    • 并发收集器
      • CMS 收集器
      • G1收集器
  • JVM优化
    • 一、常见JVM调优场景(高频考点)
    • 二、JVM调优核心步骤
    • 三、调优实战工具链
    • 四、面试加分回答要点

类加载机制

系统加载 Class 类型的文件主要三步:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析

img

加载(Loading)

  1. 通过全限定类名获取定义此类的二进制字节流。
  2. 将字节流所代表的静态存储结构转换为方法区的运行时数据结构。
  3. 在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口。

连接(Linking)‌

  • 验证(Verification):检查字节码是否符合JVM规范(文件格式、元数据、字节码、符号引用等验证)。
  • 准备(Preparation)‌:为‌静态变量‌分配内存并设置默认初始值(数据类型默认的零值)。若静态变量被final修饰且值在编译期可知,则直接赋值。
  • 解析(Resolution):将常量池中的符号引用替换为直接引用(如方法或字段的内存地址)。

初始化(Initialization)‌

  • 执行静态变量的显式赋值操作和静态代码块(static{}),合并到()方法中按顺序执行。

注意‌:加载、验证、准备、初始化的顺序固定,解析阶段可能在初始化之后(支持动态绑定)。

使用(Using)与卸载(Unloading)

  • 类实例化后进入使用阶段;当无引用且类加载器可回收时,类被卸载。

  • 卸载类需要满足 3 个要求:

    • 该类的所有的实例对象都已被 GC
    • 该类没有在其他任何地方被引用
    • 该类的类加载器的实例已被 GC

在 JVM 生命周期内,由 jvm 自带的类加载器加载的类是不会被卸载的。但是由我们自定义的类加载器加载的类是可能被卸载的。

类加载器和双亲委派模型

类加载器

JVM 中内置了三个重要的 ClassLoader

  1. BootstrapClassLoader (启动类加载器):最顶层的加载类,由 C++实现,通常表示为 null,并且没有父级,主要用来加载 JDK 内部的核心类库( %JAVA_HOME%/lib目录下的 rt.jar jar 包和类)以及被 -Xbootclasspath参数指定的路径下的所有类。
  2. ExtensionClassLoader(扩展类加载器):主要负责加载 %JRE_HOME%/lib/ext 目录下的 jar 包和类以及被 java.ext.dirs 系统变量所指定的路径下的所有类。
  3. AppClassLoader(应用程序类加载器):面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。

如果要自定义类加载器,需要继承 ClassLoader抽象类。

ClassLoader 类有两个关键的方法:

  • protected Class loadClass(String name, boolean resolve):加载指定二进制名称的类,实现了双亲委派机制 。name 为类的二进制名称,resolve 如果为 true,在加载时调用 resolveClass(Class c) 方法解析该类。
  • protected Class findClass(String name):根据类的二进制名称来查找类,默认实现是空方法。

如果我们不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。但是,如果想打破双亲委派模型则需要重写 loadClass() 方法。

双亲委派模型

img

  1. 自底向上查找判断类是否被加载
  2. 自顶向下尝试加载类

每当一个类加载器接收到加载请求时,它会先将请求委派给父类加载器。在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载。

扩展:JVM 判定两个 Java 类是否相同的具体规则:JVM 不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样

好处避免类的重复加载和防止核心 API 被篡改。

垃圾收集算法

标记-清除算法

标记-清除(Mark-and-Sweep)算法分为“标记(Mark)”和“清除(Sweep)”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。

  • 效率问题:标记和清除两个过程效率都不高。
  • 空间问题:标记清除后会产生大量不连续的内存碎片。

复制算法

为了解决标记-清除算法的效率和内存碎片问题,复制(Copying)收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用过的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

  • 可用内存变小:可用内存缩小为原来的一半。
  • 不适合老年代:如果存活对象数量比较大,复制性能会变得很差。

标记-整理算法

标记-整理(Mark-and-Compact)算法是根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

  • 适合老年代这种垃圾回收频率不是很高的场景。

分代收集算法

当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 Java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

比如在新生代中,每次收集都会有大量对象死去,所以可以选择“复制”算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

垃圾收集器

串行收集器

Serial 收集器

只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束。单线程、适合客户端应用

新生代采用标记-复制算法,老年代采用标记-整理算法。

Serial Old 收集器

Serial 收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:一种用途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使用,另一种用途是作为 CMS 收集器的后备方案。

img

并行收集器

  • 并行(Parallel)[吞吐量优先]:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。多线程高吞吐,适合后台计算

ParNew 收集器

ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。

Parallel Scavenge 收集器

Parallel Scavenge 收集器也是使用标记-复制算法的多线程收集器。Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。

Parallel Old 收集器

Parallel Scavenge 收集器的老年代版本。使用多线程和“标记-整理”算法。在注重吞吐量以及 CPU 资源的场合,都可以优先考虑 Parallel Scavenge 收集器和 Parallel Old 收集器。

img

JDK1.8 默认使用的是 Parallel Scavenge + Parallel Old

并发收集器

  • 并发(Concurrent)[停顿时间优先]:指用户线程与垃圾收集线程同时执行(但不一定是并行,可能会交替执行),垃圾收集线程在执行的时候不会停顿用户线程的运行。

CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。

CMS 收集器是一种 “标记-清除”算法实现的,它的运作过程相比于前面几种垃圾收集器来说更加复杂一些。整个过程分为四个步骤:

  • 初始标记: 短暂停顿,标记直接与 root 相连的对象(根对象);
  • 并发标记: 同时开启 GC 和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。(三色标记
  • 重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短
  • 并发清除: 开启用户线程,同时 GC 线程开始对未标记的区域做清扫。

img

从它的名字就可以看出它是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面三个明显的缺点:

  • 对 CPU 资源敏感;
  • 无法处理浮动垃圾;
  • 它使用的“标记-清除”算法会导致收集结束时会有大量空间碎片产生。

G1收集器

G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 区域化分代,平衡吞吐与停顿

被视为 JDK1.7 中 HotSpot 虚拟机的一个重要进化特征。它具备以下特点:

  • 并行与并发:G1 能充分利用 CPU、多核环境下的硬件优势,使用多个 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿时间。部分其他收集器原本需要停顿 Java 线程执行的 GC 动作,G1 收集器仍然可以通过并发的方式让 java 程序继续执行。
  • 分代收集:虽然 G1 可以不需要其他收集器配合就能独立管理整个 GC 堆,但是还是保留了分代的概念。
  • 空间整合:与 CMS 的“标记-清除”算法不同,G1 从整体来看是基于“标记-整理”算法实现的收集器;从局部上来看是基于“标记-复制”算法实现的。
  • 可预测的停顿:这是 G1 相对于 CMS 的另一个大优势,降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过 N 毫秒。

G1 收集器的运作大致分为以下几个步骤:

  • 初始标记: 短暂停顿(Stop-The-World,STW),标记从 GC Roots 可直接引用的对象,即标记所有直接可达的活跃对象
  • 并发标记:与应用并发运行,标记所有可达对象。 这一阶段可能持续较长时间,取决于堆的大小和对象的数量。
  • 最终标记: 短暂停顿(STW),处理并发标记阶段结束后残留的少量未处理的引用变更。
  • 筛选回收:根据标记结果,选择回收价值高的区域,复制存活对象到新区域,回收旧区域内存。这一阶段包含一个或多个停顿(STW),具体取决于回收的复杂度。

img

G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来) 。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)。

从 JDK9 开始,G1 垃圾收集器成为了默认的垃圾收集器。

JVM优化

一、常见JVM调优场景(高频考点)

  1. 频繁Full GC或GC停顿过长

    • 现象‌:响应延迟、吞吐量下降,GC日志显示"Concurrent Mode Failure"(CMS)或"Evacuation Failure"(G1)。
    • 原因‌:堆内存不足、大对象分配过量、新生代/老年代比例失调、垃圾回收器选型不当。
    • 优化方向‌:调整堆大小(-Xms/-Xmx)、优化分代比例(-XX:NewRatio)、切换合适GC器。
  2. 内存泄漏或OOM(OutOfMemoryError)

    • 定位工具‌:jmap -histo分析对象分布、MAT分析堆转储文件(-XX:+HeapDumpOnOutOfMemoryError)。
    • 典型原因‌:未释放大对象(如无淘汰策略的缓存)、静态集合长期持有引用、线程泄漏。
  3. 吞吐量不足或启动性能差

    • 优化手段‌:增大新生代减少Young GC(-Xmn)、调整JIT编译阈值(-XX:CompileThreshold)、类加载优化。

二、JVM调优核心步骤

  1. 数据采集与分析
  • 工具‌:

    • GC日志:-Xloggc:gc.log -XX:+PrintGCDetails → 用GCViewer/GCEasy分析。
    • 实时监控:jstat -gc [pid](GC频率/耗时)、jcmd [pid] VM.native_memory(内存分布)。
  • 关键指标‌:Young GC频率≤1次/秒、Full GC频率小时级、GC耗时占比<10%。

  1. 堆内存配置

    • -Xms-Xmx‌:生产环境建议设为相同值(如 -Xms4g -Xmx4g),避免堆动态扩容引发的Full GC。

    • 新生代比例‌:

      • 默认 -XX:NewRatio=2(老年代:新生代=2:1)。
      • 对象朝生夕死场景:增大新生代(-Xmn占堆1/3~1/2)。
  2. 垃圾回收器选型

回收器特点适用场景
Parallel GC吞吐量优先批处理、计算密集型任务
CMS低延迟(并发标记)Web服务、实时系统
G1平衡吞吐/延迟,大堆内存首选JDK9+默认,堆>4GB
ZGC/Shenandoah亚毫秒级停顿百GB级堆、金融交易系统
  1. 关键参数示例
# G1调优示例:目标停顿200ms,启用字符串去重
java -XX:+UseG1GC -Xms8g -Xmx8g -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication -jar app.jar# 元空间防OOM(Java 8+)
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m

三、调优实战工具链

  1. 监控诊断

    • JVisualVM‌:图形化监控堆/线程/GC,支持堆转储分析。
    • Arthas‌:在线诊断线程阻塞、方法执行耗时。
  2. 内存分析

    • MAT(Memory Analyzer) ‌:定位内存泄漏对象及引用链。
    • jmap‌:生成堆转储文件(jmap -dump:format=b,file=heap.hprof [pid])。

四、面试加分回答要点

  • 调优目标量化‌:明确优化方向(如“将GC停顿从500ms降至100ms内”)。

  • 结合业务场景‌:举例说明调优效果(如“电商大促前通过G1替换CMS,峰值期GC停顿降低80%”)。

  • 避坑指南‌:

    • 避免盲目设置-Xmx过大引发系统Swap。
    • 元空间(Metaspace)泄漏需关注动态类生成(如CGLib)。

持续更新中…

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DBeaver的sql编辑器文本格式字体大小设置

DBeaver的sql编辑器文本格式字体大小设置。开始就没有找到。早上比较清醒被我发现了。记录下来

自学Java怎么入门

自学Java其实没有想象中那么难&#xff0c;只要找对方法&#xff0c;循序渐进地学习&#xff0c;很快就能上手。下面我结合自己的经验&#xff0c;给你整理一条清晰的学习路径&#xff0c;咱们一步步来。 一、先了解Java能做什么 在开始之前&#xff0c;建议你先看看Java都能…

操作系统面试知识点(1):操作系统基础

目录 1.什么是操作系统: 2.操作系统有哪些功能? 3.常见的操作系统有哪些 4.用户态和内核态 5.内核态权限这么高,为什么不还要用户态? 6.用户态和内核态是如何切换的? 7.系统调用 8,系统调用的过程 ​​​​​​​1.什么是操作系统: (1)操作系统(OS)是管理计算机软硬…

Linux 和 Windows 服务器:哪一个更适合您的业务需求?

在选择服务器操作系统时&#xff0c;Linux 和 Windows 服务器是最常见的两种选择。它们各有特点&#xff0c;适合不同的业务需求和使用环境。本文将详细分析 Linux 和 Windows 服务器的主要差异&#xff0c;包括成本、安全性、性能、定制化能力和用户界面等方面&#xff0c;帮助…

CertiK联创顾荣辉将于港大活动发表演讲,分享Web3安全与发展新视角

CertiK联合创始人、哥伦比亚大学教授顾荣辉&#xff0c;将于6月30日出席香港大学经管学院主办的“Web3革新与商业机遇”活动&#xff0c;并发表主题演讲《规模化Web3&#xff1a;面向全球受众的创新与安全平衡之道》。 本次活动由港大经管学院高层管理教育主办&#xff0c;Met…

SpringAI系列---【SpringAI如何接入阿里云百炼大模型?】

1.导言 阿里云百炼的通义千问模型支持 OpenAI 兼容接口&#xff0c;您只需调整 API Key、BASE_URL 和模型名称&#xff0c;即可将原有 OpenAI 代码迁移至阿里云百炼服务使用。 如果是要接入阿里云百炼模型&#xff0c;首先推荐使用Spring AI Alibaba&#xff0c;而不是使用Spri…

电子电气架构 --- 实时系统评价的概述

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

Flink SQL执行流程深度剖析:从SQL语句到分布式执行

在大数据处理领域&#xff0c;Flink SQL凭借其强大的处理能力和易用性&#xff0c;成为众多开发者的选择。与其他OLAP引擎类似&#xff0c;Flink SQL的SQL执行流程大致都需要经过词法解析、语法解析、生成抽象语法树&#xff08;AST&#xff09;、校验以及生成逻辑执行计划等步…

什么是redis

Redis是一个开源的、基于内存的高性能键值存储数据库&#xff0c;广泛用于缓存、消息队列、会话存储等场景。 - 核心特点&#xff1a; - 内存存储&#xff1a;数据存储在内存中&#xff0c;读写速度极快。 - 键值对&#xff1a;以键值对形式存储数据&#xff0c;键通常是字…

《从0到1:C/C++音视频开发自学指南》

开启自学之旅&#xff1a;为何选择 C/C 音视频开发 在当今数字化时代&#xff0c;音视频开发的应用场景极为广泛&#xff0c;深刻融入了我们生活与工作的方方面面。从火爆的直播行业&#xff0c;无论是电商直播中主播与观众的实时互动&#xff0c;还是游戏直播里精彩赛事的实时…

学习日记-spring-day37-6.25

知识点&#xff1a; 1.使用utillist进行配置 知识点 核心内容 重点 Spring框架中utl名称空间创建List 通过utl名称空间创建并管理集合对象&#xff0c;实现数据复用 utl list与普通list赋值的区别; 名称空间引入方法 无参构造器使用规则 当类中没有其他构造器时&#x…

【Python练习】012. 使用字符串的upper()方法将字符串转换为大写

012. 使用字符串的upper方法将字符串转换为大写 012. 使用字符串的upper()方法将字符串转换为大写示例代码运行结果代码解释 扩展&#xff1a;动态输入字符串示例运行 何时使用upper方法基本用法示例忽略大小写的字符串比较数据清洗标准化 注意事项 012. 使用字符串的upper()方…

Python Polars库详解:高性能数据处理的新标杆

在数据驱动的时代&#xff0c;高效的数据处理能力已成为开发者和数据科学家的核心竞争力。作为Pandas的强劲挑战者&#xff0c;Polars库凭借其基于Rust的底层架构和创新的表达式引擎&#xff0c;在性能测试中展现出惊人的速度优势。本文将深入解析Polars的核心特性、使用技巧及…

Go语言- 单元测试

实际开发中&#xff0c;需要保证单元功能正确。 传统方式&#xff1a;在main函数中直接调用&#xff0c;查看结合是否和预期一致。 缺点&#xff1a;1. 不方便 2. 不利于管理 因此&#xff0c;单元测试具有必要性 testing测试框架 Go语言中自带testing轻量级测试框架和go…

Vue移动端开发的适配方案与性能优化技巧

文章目录 1. 移动端适配方案1.1. 视口适配1.2. 基于rem/em的适配方案1.3. vw/vh视口单位适配1.4. 移动端UI组件库适配 2. 移动端性能优化技巧2.1. 虚拟列表实现长列表优化2.2. 图片懒加载与优化2.3. 减少首屏加载时间2.4. 事件节流与防抖 3. 移动端常见问题解决方案3.1. 移动端…

如何微调和部署OpenVLA在机器人平台上

这个教程来自这个英伟达网址 教程的目标是提供用于部署 VLA 模型的优化量化和推理方法&#xff0c;以及针对新机器人、任务和环境的参考微调流程。在一个自包含的仿真环境中&#xff0c;结合场景生成和领域随机化&#xff08;MimicGen&#xff09;对性能和准确性进行严格验证。…

深入剖析Flink内存管理:架构、调优与实战指南

在大数据处理领域&#xff0c;Apache Flink凭借强大的流处理和批处理能力备受青睐。而Flink内存管理机制&#xff0c;作为保障作业高效稳定运行的关键支柱&#xff0c;深刻影响着任务执行性能、资源利用率以及系统容错能力。理解并掌握Flink内存管理原理与优化策略&#xff0c;…

【力扣 C】动态规划专题目录

【力扣 简单 C】509. 斐波那契数https://blog.csdn.net/2503_92320911/article/details/148810148 【力扣 中等 C】983. 最低票价https://blog.csdn.net/2503_92320911/article/details/148833421 【力扣 中等 C】91. 解码方法https://blog.csdn.net/2503_92320911/article/d…

Linux 中如果网络连接丢失或无法找到网络设备

如下步骤 1. 检查网络服务状态 sudo systemctl status NetworkManager 如果服务未运行&#xff0c;启动并启用它&#xff1a; sudo systemctl start NetworkManager sudo systemctl enable NetworkManager ______ 2. 检查网络接口 ip add 确认网卡&#xff08;如 eth0、en…

【Linux 平台总线驱动开发实战】

Linux 平台总线驱动开发实战 一、平台总线驱动基础概念二、核心数据结构解析2.1 设备结构体 struct platform_device2.2 驱动结构体 struct platform_driver2.3 资源结构体 struct resource 三、驱动开发完整流程3.1 设备注册3.2 驱动注册3.3 设备与驱动匹配 四、编译与测试4.1…