Java 大厂面试题 -- JVM 深度剖析:解锁大厂 Offe 的核心密钥
最近佳作推荐:
Java大厂面试高频考点|分布式系统JVM优化实战全解析(附真题)(New)
Java大厂面试题 – JVM 优化进阶之路:从原理到实战的深度剖析(2)(New)
Java大厂面试题 – 深度揭秘 JVM 优化:六道面试题与行业巨头实战解析(1)(New)
开源架构与人工智能的融合:开启技术新纪元(New)
开源架构的自动化测试策略优化版(New)
开源架构的容器化部署优化版(New)
开源架构的微服务架构实践优化版(New)
开源架构中的数据库选择优化版(New)
开源架构学习指南:文档与资源的智慧锦囊(New)
个人信息:
微信公众号:开源架构师
微信号:OSArch
我管理的社区推荐:【青云交技术福利商务圈】和【架构师社区】
2025 CSDN 博客之星 创作交流营(New):点击快速加入
推荐青云交技术圈福利社群:点击快速加入
Java 大厂面试题 -- JVM 深度剖析:解锁大厂 Offe 的核心密钥
- 引言
- 正文
- 一、垃圾回收机制
- 1.1 垃圾回收的基本概念
- 1.2 常见的垃圾回收算法
- 1.2.1 标记 - 清除算法
- 1.2.2 复制算法
- 1.2.3 标记 - 整理算法
- 1.2.4 各算法对比图表
- 二、类加载器原理
- 2.1 类加载器的作用
- 2.2 类加载器的分类
- 2.2.1 启动类加载器(Bootstrap ClassLoader)
- 2.2.2 扩展类加载器(Extension ClassLoader)
- 2.2.3 应用程序类加载器(Application ClassLoader)
- 2.3 类加载的双亲委派模型
- 2.4 类加载器关系图表
- 三、JVM 性能监控工具及优化实战
- 3.1 JConsole
- 3.2 VisualVM
- 3.3 优化实战案例
- 四、JVM 在不同云环境及最新 Java 版本下的表现与优化
- 4.1 JVM 在主流云环境中的特性与优化要点
- 4.1.1 AWS(Amazon Web Services)
- 4.1.2 阿里云
- 4.2 结合最新 Java 版本(如 Java 24)的 JVM 特性改进
- 4.2.1 Java 24 的创新突破
- 4.2.2 Java 24 对高并发及启动性能的优化
- 结束语
- 🎯欢迎您投票
引言
亲爱的开源构架技术伙伴们!大家好!在当今竞争激烈的技术求职领域,大厂 Offer 无疑是众多开发者梦寐以求的荣耀。而 Java 虚拟机(JVM)作为 Java 技术体系的核心,在大厂面试中始终占据着举足轻重的地位,堪称开启大厂之门的 “金钥匙”。深入探究 JVM 的底层运作机制,熟练掌握其性能优化技巧,是每一位渴望在大厂崭露头角的开发者的必备技能。接下来,让我们一同深入挖掘 JVM 的奥秘,为斩获心仪的大厂 Offer 筑牢根基。
正文
一、垃圾回收机制
1.1 垃圾回收的基本概念
垃圾回收(Garbage Collection,GC)是 JVM 自动内存管理的核心机制,形象地说,它就如同 Java 程序的 “智能内存管家”。在 Java 程序运行过程中,对象被不断创建和使用,如同舞台上活跃的演员。但当某些对象不再被程序中的任何有效引用关联时,它们便成为 “垃圾” 对象,其所占用的内存空间亟待回收,以便重新利用。这一自动化的内存管理机制,极大地解放了开发者,使其无需手动处理繁琐的内存管理事务,有效避免了内存泄漏、悬空指针等常见问题,让开发者能够专注于业务逻辑的实现与优化。
1.2 常见的垃圾回收算法
1.2.1 标记 - 清除算法
原理:该算法如同一场有条不紊的 “内存大扫除”,分为标记与清除两个阶段。在标记阶段,垃圾回收器就像一位严谨的 “侦探”,从根对象(例如栈中的局部变量、静态变量以及方法区中的类静态变量等)出发,沿着对象的引用链进行深度遍历,精确标记出所有被引用的对象。而在清除阶段,它则化身为高效的 “清洁工”,将所有未被标记的对象视为 “垃圾”,回收其占用的内存空间,使内存得以 “焕然一新”。
示例代码:
// 模拟对象创建与标记 - 清除过程
class ObjectExample {// 定义一个私有成员变量data,用于模拟对象所携带的数据private int data;// 构造函数,用于初始化对象的data成员变量public ObjectExample(int data) {this.data = data;}
}public class MarkSweepExample {public static void main(String[] args) {// 创建三个ObjectExample对象,分别为obj1、obj2和obj3ObjectExample obj1 = new ObjectExample(1);ObjectExample obj2 = new ObjectExample(2);ObjectExample obj3 = new ObjectExample(3);// 将obj2的引用设置为null,模拟obj2不再被引用,从而使其成为垃圾对象obj2 = null;// 这里虽然没有显式地手动触发垃圾回收操作,但在程序运行的某个时刻,JVM会根据自身的垃圾回收策略自动进行垃圾回收// 届时,垃圾回收器会标记obj1和obj3为存活对象,然后清除obj2所占用的内存空间}
}
优缺点分析:
-
优点:标记 - 清除算法的实现思路相对简单直观,易于理解。在对象数量较少且对内存连续性要求不高的场景下,能够较为便捷地实现垃圾回收功能,就像在一个小房间里清理杂物,操作相对轻松。
-
缺点:然而,该算法存在较为明显的缺陷。一方面,它会产生大量不连续的内存碎片,随着程序的持续运行,这些碎片会使内存空间变得支离破碎,如同被分割成无数小块的拼图。这将导致后续在分配大对象时,可能因无法找到足够大的连续内存区域而提前触发垃圾回收,严重影响系统性能。另一方面,标记和清除两个阶段都需要对内存中的对象进行遍历,在对象数量庞大时,这一过程会消耗大量的时间和资源,导致垃圾回收效率低下。
1.2.2 复制算法
原理:复制算法采用了一种独特的 “空间换效率” 策略。它将可用内存平均划分为大小相等的两块区域,我们不妨将其想象为两个独立的 “内存仓库”。在程序运行过程中,每次仅使用其中一块区域来存储对象。当这块区域的内存被使用殆尽时,垃圾回收器便开始工作。它会将存活的对象逐一复制到另一块未被使用的区域中,就像将一个仓库中的有用物品搬运到另一个仓库。复制完成后,原来使用的那块区域将被彻底清空,以便下次使用。通过这种方式,不仅实现了垃圾回收,还保证了内存空间的连续性。
示例代码:
// 简单模拟复制算法的数据迁移过程
class CopyingAlgorithmExample {// 定义一个常量SIZE,表示内存区域的大小private static final int SIZE = 10;// 定义两个数组fromSpace和toSpace,分别模拟两个内存区域private int[] fromSpace = new int[SIZE];private int[] toSpace = new int[SIZE];// 定义两个索引变量fromIndex和toIndex,分别用于记录fromSpace和toSpace中已使用的位置private int fromIndex = 0;private int toIndex = 0;// 模拟对象存储的方法,将一个整数对象存储到fromSpace中public void storeObject(int object) {// 如果fromSpace已满,则调用copyObjects方法进行数据迁移if (fromIndex >= SIZE) {copyObjects();}// 将对象存储到fromSpace的当前位置,并将fromIndex向后移动一位fromSpace[fromIndex++] = object;}// 数据迁移的方法,将fromSpace中的存活对象复制到toSpace中,并交换两个内存区域的角色private void copyObjects() {// 遍历fromSpace,将存活的对象复制到toSpace中for (int i = 0; i < fromIndex; i++) {toSpace[toIndex++] = fromSpace[i];}// 清空fromSpace,将fromIndex重置为0fromIndex = 0;// 交换fromSpace和toSpace,使得原来的toSpace成为新的fromSpace,原来的fromSpace成为新的toSpaceint[] temp = fromSpace;fromSpace = toSpace;toSpace = temp;// 将toIndex重置为0,为下一次存储做准备toIndex = 0;}
}
优缺点分析:
-
优点:复制算法最大的优势在于能够彻底解决内存碎片问题,保证内存空间始终保持连续,这对于需要频繁分配大对象的应用场景来说至关重要。此外,复制算法的复制过程相对高效,只需对存活对象进行一次遍历和复制操作,在对象存活率较低的情况下,性能表现尤为出色,如同在一个物品较少的仓库中搬运物品,速度较快。
-
缺点:该算法的主要缺点是内存利用率较低,因为始终有一半的内存空间处于闲置状态,就像有一半的仓库一直空着未被利用。这在对内存资源极为敏感、内存容量有限的场景下,可能会造成内存资源的浪费,影响系统的整体性能。
1.2.3 标记 - 整理算法
原理:标记 - 整理算法可以看作是标记 - 清除算法的优化升级版。它同样先进行标记阶段,垃圾回收器从根对象开始,沿着对象的引用链进行深度遍历,标记出所有存活的对象。与标记 - 清除算法不同的是,在标记完成后,它并不会直接清除垃圾对象,而是进入整理阶段。在整理阶段,垃圾回收器会将所有存活的对象向内存的一端移动,使存活对象紧密排列在一起,就像将房间里的物品整齐地摆放到一个角落。然后,清理掉存活对象所在位置之后的所有内存空间,即清除掉那些标记为垃圾的对象所占用的内存,从而完成垃圾回收的过程。
示例代码:
// 模拟标记 - 整理算法中的对象移动过程
class MarkCompactAlgorithmExample {// 定义一个常量SIZE,表示内存数组的大小private static final int SIZE = 10;// 定义一个Object类型的数组objects,用于模拟内存中的对象存储private Object[] objects = new Object[SIZE];// 定义一个变量usedSize,用于记录已经使用的内存位置private int usedSize = 0;// 模拟对象存储的方法,将一个对象存储到objects数组中public void storeObject(Object object) {// 如果内存已满,则调用markAndCompact方法进行标记和整理操作if (usedSize >= SIZE) {markAndCompact();}// 将对象存储到objects数组的当前位置,并将usedSize加1objects[usedSize++] = object;}// 标记和整理的方法,用于标记存活对象并将其移动到内存的一端private void markAndCompact() {// 定义一个变量lastIndex,用于记录整理后存活对象的最后位置int lastIndex = 0;// 遍历objects数组,将存活的对象移动到数组的前面for (int i = 0; i < usedSize; i++) {if (objects[i] != null) {objects[lastIndex++] = objects[i];}}// 将lastIndex之后的位置设置为null,即清除垃圾对象for (int i = lastIndex; i < usedSize; i++) {objects[i] = null;}// 更新usedSize为整理后存活对象的数量usedSize = lastIndex;}
}
优缺点分析:
-
优点:标记 - 整理算法既避免了标记 - 清除算法中产生内存碎片的问题,又提高了内存利用率,相比复制算法,它不需要额外的一半内存空间来进行对象复制。在存活对象较多的情况下,它能够有效地将存活对象整理到一起,为后续的内存分配提供连续的内存空间,从而提高系统的性能和内存使用效率。此外,它在处理大规模对象时,由于不需要像复制算法那样进行大量的对象复制操作,所以性能表现也较为出色。
-
缺点:然而,标记 - 整理算法也并非十全十美。在整理阶段,需要对存活对象进行移动操作,这可能会对程序的运行产生一定的影响,尤其是在一些对对象地址敏感的应用场景中。此外,该算法的实现相对复杂,需要更多的计算资源和时间来完成标记和整理的过程。
1.2.4 各算法对比图表
为了更直观、清晰地对比这三种常见的垃圾回收算法,我们制作了如下表格:
算法名称 | 是否产生内存碎片 | 内存利用率 | 适用场景 | 实现复杂度 |
---|---|---|---|---|
标记 - 清除算法 | 是 | 较高(但因碎片问题可能导致大对象分配困难) | 对象存活周期短、对象数量相对较少且对内存连续要求不高的场景 | 较低 |
复制算法 | 否 | 低(始终有一半内存闲置) | 对象存活率低、新生代等对象创建频繁且存活时间短的场景 | 中等 |
标记 - 整理算法 | 否 | 高 | 对象存活率高、老年代等存活对象较多且对内存连续要求高的场景 | 较高 |
二、类加载器原理
2.1 类加载器的作用
类加载器在 Java 程序的运行过程中扮演着不可或缺的重要角色,堪称 Java 程序的 “类搬运工” 与 “类解析器”。其主要职责是将存储在磁盘上的字节码文件(.class 文件)准确无误地加载到 JVM 的内存中,并进一步将这些字节码文件解析、转换为运行时的类对象。这些类对象包含了类的各种信息,如类的成员变量、方法、继承关系等,为程序的运行提供了必要的类型信息支持。通过类加载器,Java 实现了类的动态加载机制,使得程序在运行时能够根据实际需求灵活地加载所需的类,而无需在程序启动时就加载所有的类。这不仅提高了程序的启动速度,还增强了程序的灵活性和可扩展性。同时,类加载器还保证了不同类之间的隔离性,每个类加载器加载的类都在其自己的命名空间内,避免了类的冲突和混乱,确保了 Java 程序能够稳定、可靠地运行。
2.2 类加载器的分类
2.2.1 启动类加载器(Bootstrap ClassLoader)
特点与职责:启动类加载器是类加载器体系中最顶层、最核心的存在,它由 C++ 语言编写而成,具有最高的权限和信任级别。启动类加载器主要负责加载 JVM 运行所必需的核心类库,这些类库是 JVM 正常运行的基础,包含了 Java 语言的核心类,如 java.lang 包中的 Object、String、Integer 等类,以及 java.util、java.io 等包中的基础类。这些类库通常位于 JDK 的 jre/lib 目录下,并且被 JVM 视为具有特殊信任级别的类。启动类加载器所加载的类存放在 JVM 的根类加载器空间中,它是其他类加载器的根基,为整个 Java 运行环境提供了最基本、最关键的支持。由于其重要性和特殊性,启动类加载器在 JVM 启动时就已经初始化并开始工作,其他类加载器的加载操作都依赖于它所提供的基础类库。
2.2.2 扩展类加载器(Extension ClassLoader)
特点与职责:扩展类加载器是由 Java 语言实现的,它继承自 java.lang.ClassLoader 类,在类加载器体系中处于启动类加载器的下一层。扩展类加载器的主要职责是加载 JRE 扩展目录(通常是 jre/lib/ext 目录)下的类库。这些类库用于扩展 JVM 的功能,提供了一些额外的、通用的功能和特性。例如,一些第三方的通用工具类库、安全相关的类库等,如果被放置在扩展目录下,就会由扩展类加载器负责加载。通过加载这些扩展类库,JVM 能够在标准核心类库的基础上,增加更多的功能,满足不同应用场景的需求。同时,扩展类加载器加载的类与启动类加载器加载的核心类库相互隔离,不会影响核心类库的稳定性和安全性。
2.2.3 应用程序类加载器(Application ClassLoader)
特点与职责:应用程序类加载器,也被称为系统类加载器,同样是由 Java 语言实现的。它是我们日常开发中最常用的类加载器,负责加载应用程序的类路径(ClassPath)下的类。当我们通过命令行运行 Java 程序时,JVM 会默认使用应用程序类加载器来加载我们编写的主类以及该类所依赖的其他类。例如,我们自己编写的业务逻辑类、自定义的工具类、各种框架的应用类等,只要它们位于正确的类路径下,都会由应用程序类加载器加载到 JVM 中。应用程序类加载器是应用程序类的默认加载器,它在类加载过程中起着关键的作用,确保了应用程序能够正常运行。同时,它也是用户自定义类加载器的父类加载器(在没有指定父类加载器的情况下),在类加载的双亲委派模型中扮演着重要的角色。
2.3 类加载的双亲委派模型
模型原理:双亲委派模型是类加载器之间一种非常重要且巧妙的协作模式,它的核心思想是:当一个类加载器收到类加载请求时,它首先不会立即尝试自己去加载该类,而是将这个请求委托给它的父类加载器去处理。父类加载器接收到请求后,同样会遵循这个规则,将请求继续向上委托给它的父类加载器,以此类推,直到请求被委托到最顶层的启动类加载器。只有当父类加载器在其负责的类路径下无法找到对应的字节码文件,即无法完成类的加载时,子类加载器才会尝试自行加载该类。这种层层委托的方式,就像是公司中的层级汇报机制,基层员工遇到问题先向上级汇报,上级再依次向上汇报,直到找到能解决问题的层级为止,如果上级都无法解决,基层员工才会自己想办法。
示例代码:
// 自定义类加载器,用于展示双亲委派模型的工作机制
class CustomClassLoader extends ClassLoader {// 构造函数,调用父类构造函数,默认使用系统类加载器作为父加载器public CustomClassLoader() {super();}@Override// 重写findClass方法,用于查找并加载类protected Class<?> findClass(String name) throws ClassNotFoundException {try {// 首先尝试让父类加载器加载类return super.loadClass(name);} catch (ClassNotFoundException e) {// 如果父类加载器无法加载,执行自定义加载逻辑// 这里简单示例从当前目录下读取字节码文件byte[] classData = loadClassData(name);if (classData == null) {// 如果读取字节码文件失败,抛出类未找到异常throw new ClassNotFoundException(name);} else {// 使用defineClass方法将字节码数据转换为类对象return defineClass(name, classData, 0, classData.length);}}}private byte[] loadClassData(String name) {// 模拟从文件系统读取字节码文件,实际应用中需要更完善的文件读取逻辑try {// 将类名转换为文件路径格式,例如将"com.example.MyClass"转换为"com/example/MyClass.class"String fileName = name.replace('.', '/') + ".class";// 创建文件输入流,用于从文件中读取字节码数据InputStream inputStream = new FileInputStream(fileName);// 创建字节数组输出流,用于将从文件中读取的数据存储到字节数组中ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();int data;// 循环读取文件中的数据,每次读取一个字节,并将其写入字节数组输出流while ((data = inputStream.read()) != -1) {byteArrayOutputStream.write(data);}// 关闭文件输入流,释放资源inputStream.close();// 将字节数组输出流中的数据转换为字节数组并返回return byteArrayOutputStream.toByteArray();} catch (IOException e) {// 如果在读取文件过程中发生异常,打印异常堆栈信息e.printStackTrace();// 返回null表示读取字节码文件失败return null;}}
}
优点:
-
安全性:双亲委派模型为 Java 核心类库构建了坚固的防护壁垒,有力保障了其安全性。由于启动类加载器会优先加载核心类库,这就使得用户自定义类难以覆盖像java.lang.Object这类关键核心类。例如,若有人试图自定义一个Object类,在双亲委派模型下,启动类加载器早已加载了真正的java.lang.Object,自定义类无法生效,从而有效维护了 Java 运行环境的稳定,防止核心类库被恶意篡改,确保系统安全可靠运行。
-
避免重复加载:该模型巧妙解决了类的重复加载问题。当某个类被某类加载器成功加载后,后续针对该类的加载请求都会复用已加载的类。这大大提升了类加载效率,避免了因重复加载类而造成的内存资源浪费,如同图书馆中已借阅的书籍可直接供他人使用,无需再次购置,有效提升了系统整体性能。
2.4 类加载器关系图表
为了更直观地呈现类加载器之间的层级关系以及双亲委派模型的工作流程,我们通过以下图表来展示:
在这张图表中,启动类加载器位于最顶层,是整个类加载器体系的根基,如同大树的主干。扩展类加载器处于启动类加载器的下一层,依赖启动类加载器加载的核心类库工作,类似大树的主要枝干。应用程序类加载器在扩展类加载器之下,负责加载应用程序类路径下的类,如同大树的分支。而自定义类加载器(这里以CustomClassLoader为例),通常继承自ClassLoader类,在应用程序类加载器基础上扩展,用于满足特定类加载需求,就像大树分支上长出的新枝丫。箭头方向清晰展示了类加载请求的委派路径,当类加载器收到类加载请求时,会沿箭头向上委派给父类加载器,直到启动类加载器。若父类加载器无法完成加载,子类加载器才尝试自行加载,生动体现了双亲委派模型的运作机制。
三、JVM 性能监控工具及优化实战
3.1 JConsole
工具介绍:JConsole 可谓是 JDK 贴心附赠的一款可视化 JVM 监控利器,无需额外安装,开箱即可使用。它依托 JMX(Java Management Extensions)技术,就像为运行中的 Java 应用程序安装了一套实时监控摄像头,能够全方位、实时获取 JVM 的各项关键运行数据,包括内存使用状况、线程活跃状态、类加载进展、CPU 使用率等多个维度,为开发者洞悉 JVM 内部运行态势提供了直观窗口。
使用示例:
- 启动一个简单的 Java 程序,例如:
public class JConsoleExample {public static void main(String[] args) {while (true) {// 模拟程序持续运行,频繁进行内存分配操作byte[] data = new byte[1024 * 1024];try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}
}
-
开启 JConsole,在 “本地进程” 列表中精准锁定刚刚启动的程序进程,点击 “连接” 按钮,即可建立起与目标程序的监控桥梁。
-
在 JConsole 界面中,通过各个精心设计的选项卡,如同切换不同监控视角,查看丰富多样的监控数据。以 “内存” 选项卡为例,能够实时洞察 Eden 区、Survivor 区、老年代等内存区域的使用量变化趋势,以及垃圾回收的触发频次和每次回收所耗费的时间。通过深度剖析这些数据,开发者能够精准判断 JVM 的内存分配策略是否契合程序实际需求,是否存在因频繁创建小对象导致 Eden 区迅速填满,进而频繁触发 Minor GC 的隐患,亦或是存在对象长时间滞留内存无法回收,致使老年代内存占用居高不下的棘手问题。
3.2 VisualVM
工具介绍:VisualVM 则是一款功能更为强大、全面的 JVM 性能分析超级武器。它不仅无缝继承了 JConsole 的基本监控功能,还进一步拓展了更深入的采样分析能力,涵盖 CPU 采样和内存采样两大关键领域。借助 CPU 采样,能够如同使用高精度 CT 扫描仪,精准定位应用程序中 CPU 消耗大户的方法,助力开发者快速锁定性能瓶颈的症结所在;内存采样则能够深入拆解对象的内存占用细节,敏锐发现内存泄漏的蛛丝马迹,通过细致查看对象的引用关系,如同追踪复杂的人际关系网络,精准揪出那些长时间霸占内存且未被释放的顽固对象,为内存优化工作提供极具价值的线索和依据。
使用示例:
- 启动一个具备一定业务复杂度的 Java 应用程序,假设这是一个基于 Jetty 框架搭建的简单 Web 服务器应用:
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class VisualVMExample {public static void main(String[] args) throws Exception {Server server = new Server(8080);ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);context.setContextPath("/");server.setHandler(context);context.addServlet(new ServletHolder(new HelloWorldServlet()), "/hello");server.start();server.join();}public static class HelloWorldServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 模拟业务逻辑,可能存在性能问题的代码for (int i = 0; i < 1000000; i++) {// 进行一些复杂计算,消耗CPU资源double result = Math.sqrt(i);}response.getWriter().println("Hello, World!");}}
}
-
打开 VisualVM 工具,在左侧 “应用程序” 列表中迅速定位正在运行的该应用程序进程,双击即可打开全面且详细的监控界面,开启对应用程序性能的深度剖析之旅。
- 进行 CPU 采样:在 VisualVM 界面中点击 “采样” 选项卡,选择 “CPU”,随后点击 “开始” 按钮。此刻,VisualVM 宛如启动了一台高性能的性能探测器,开始对应用程序的 CPU 使用情况展开细致入微的采样分析。经过一段时间的数据收集,点击 “停止” 按钮,在生成的采样结果中,能够清晰地看到各个方法的 CPU 占用时间和调用次数。例如,在上述代码中,通过 CPU 采样可以明确发现HelloWorldServlet类中的doGet方法在进行复杂计算时占用了大量的 CPU 时间,从而确定这是一个可能的性能优化点。
- 进行内存采样:同样在 “采样” 选项卡中,选择 “内存”,点击 “开始” 按钮。VisualVM 会即刻对应用程序的内存使用情况展开深度挖掘,包括对象的创建、存活和销毁等全生命周期的情况。通过深入分析内存采样结果,可以敏锐地察觉到是否存在对象创建后未被正确释放的问题。例如,在一个长时间运行的 Web 应用中,如果发现某些对象在请求处理完成后仍然被不合理地持有引用,导致内存泄漏,通过内存采样可以清晰地看到这些对象的引用链,从而定位到问题根源并进行修复。
3.3 优化实战案例
案例背景:假设有一个大型的在线交易系统,随着业务量呈井喷式增长,用户数量和交易频率大幅提升,系统逐渐暴露出响应迟缓的问题,严重影响了用户体验,甚至对业务的持续增长构成了威胁。经过初步的系统排查和分析,怀疑 JVM 性能问题是导致这一状况的主要原因。
性能分析过程:
- 利用 VisualVM 洞察 CPU 性能瓶颈:使用 VisualVM 对在线交易系统进行全面的性能监控。通过 CPU 采样发现,在处理交易请求的核心业务方法中,存在大量复杂的计算和低效的数据库查询操作,这些操作犹如一个个性能杀手,导致 CPU 使用率长期处于高位。例如,在计算订单总价时,采用了多层嵌套循环遍历海量的订单明细数据,这种方式在数据量较大时,效率极其低下;并且在查询商品信息时,每次都进行全表扫描,完全没有利用数据库索引这一高速通道,使得查询速度缓慢,进一步加重了 CPU 的负担。
- 借助 VisualVM 揪出内存顽疾:内存采样结果显示,垃圾回收频繁发生,尤其是老年代内存占用如同失控的气球一般持续上升。进一步深入分析发现,系统中存在一些对象在创建后长时间未被释放。例如,在处理交易事务时,创建了大量用于记录事务日志的对象,然而在事务完成后,这些日志对象的引用没有被及时清理,导致它们一直存活在内存中,逐渐占用大量的老年代空间,引发频繁的 Full GC,严重拖累了系统性能。
优化措施:
- 算法与数据库查询优化:
- 算法优化:订单总价计算大变身:针对计算订单总价时的低效率嵌套循环,引入 Java 8 的流操作进行优化。将原本繁琐、低效的多层循环转换为简洁、高效的函数式编程风格。优化前的代码就像一条蜿蜒曲折、充满阻碍的小路:
// 原始计算订单总价的方式,使用嵌套循环
class OrderItem {private double price;private int quantity;public OrderItem(double price, int quantity) {this.price = price;this.quantity = quantity;}public double getPrice() {return price;}public int getQuantity() {return quantity;}
}public class OrderTotalCalculationOld {public static void main(String[] args) {OrderItem[] orderItems = new OrderItem[]{new OrderItem(10.0, 2),new OrderItem(15.0, 3)};double totalPrice = 0;for (int i = 0; i < orderItems.length; i++) {OrderItem item = orderItems[i];totalPrice += item.getPrice() * item.getQuantity();}System.out.println("Total price: " + totalPrice);}
}
优化后,利用流操作,代码变得像一条笔直、通畅的高速公路:
import java.util.ArrayList;
import java.util.List;class OrderItem {private double price;private int quantity;public OrderItem(double price, int quantity) {this.price = price;this.quantity = quantity;}// 计算单个订单项的总价public double getTotal() {return price * quantity;}
}public class OrderTotalCalculation {public static void main(String[] args) {List<OrderItem> orderItems = new ArrayList<>();orderItems.add(new OrderItem(10.0, 2));orderItems.add(new OrderItem(15.0, 3));// 使用流操作计算订单总价double totalPrice = orderItems.stream().mapToDouble(OrderItem::getTotal).sum();System.out.println("Total price: " + totalPrice);}
}
在优化后的代码中,stream()方法将orderItems列表巧妙地转换为流,mapToDouble(OrderItem::getTotal)方法就像一个高效的转换器,对每个OrderItem对象精准地应用getTotal方法,将其转换为对应的总价数值流,最后通过sum()方法轻松地对数值流进行求和,迅速得到订单的总价。流操作充分利用了 Java 的并行计算框架,在多核处理器环境下能够自动并行处理数据,大大提高了计算效率,尤其在处理大规模数据时优势更为明显。
- 数据库查询优化:商品信息查询的 “加速升级”:在数据库查询方面,为商品信息表添加合适的索引是提升性能的关键一步。以 MySQL 数据库为例,假设商品信息表products的结构如下:
字段名 | 数据类型 | 描述 |
---|---|---|
product_id | INT | 商品唯一标识 |
product_name | VARCHAR (255) | 商品名称 |
price | DECIMAL (10, 2) | 商品价格 |
如果系统经常根据product_id查询商品信息,而未对该字段创建索引,数据库在执行查询时,就像在茫茫大海中盲目搜索一艘船,需要全表扫描每一条记录来匹配目标product_id,随着数据量的增长,查询耗时会呈指数级增加。例如,执行查询语句SELECT * FROM products WHERE product_id = 123;,未创建索引时,数据库的查询效率极低。为了改善这种情况,通过以下 SQL 语句为product_id字段添加索引:
CREATE INDEX idx_product_id ON products (product_id);
创建索引后,数据库会构建一个基于product_id的高效数据结构(如 B - Tree 索引),在执行上述查询时,数据库可以借助索引快速定位到目标记录,极大地缩短了查询时间,有效降低了数据库服务器的 CPU 负载,为系统整体性能的提升注入了强大的动力。
- 内存管理优化:
- 解决事务日志对象内存泄漏:斩断内存 “枷锁”:对于事务日志对象的内存泄漏问题,在事务完成后及时清理相关引用是关键。在代码中,利用try - finally块这一安全保障机制,确保事务日志对象在使用完毕后被妥善释放。以下是优化后的事务处理代码示例:
import java.util.ArrayList;
import java.util.List;class TransactionLog {// 模拟事务日志记录,包含日志信息private String logMessage;public TransactionLog(String logMessage) {this.logMessage = logMessage;}
}public class TransactionManager {public static void main(String[] args) {List<TransactionLog> transactionLogs = new ArrayList<>();try {// 模拟事务开始,创建事务日志对象记录起始信息TransactionLog log1 = new TransactionLog("Transaction started");transactionLogs.add(log1);// 模拟事务执行中的一些操作,如数据库读写// 这里省略实际数据库操作代码,仅作示意// 模拟事务结束,创建事务日志对象记录结束信息TransactionLog log2 = new TransactionLog("Transaction completed");transactionLogs.add(log2);} finally {// 事务完成后,果断清理事务日志对象引用,防止内存泄漏transactionLogs.clear();}}
}
在上述代码里,try - finally块如同一位尽责的管家,无论事务执行过程中是否遭遇异常,在事务结束时都会坚定不移地执行transactionLogs.clear()操作,将transactionLogs列表中的所有事务日志对象引用彻底清空。如此一来,当垃圾回收器启动工作时,这些失去引用的事务日志对象就会被顺利回收,避免了它们长期霸占内存,有效防止老年代内存因这些冗余对象的不断堆积而被耗尽,大幅减少 Full GC 的触发频率,显著提高系统内存使用效率。
- JVM 内存参数优化:定制专属内存 “套餐”:合理配置 JVM 内存参数对系统性能的提升至关重要。以该在线交易系统为例,假设系统运行在一台配备 4GB 内存的服务器上,且业务特性显示短生命周期对象众多。在默认情况下,JVM 的堆内存大小和新生代、老年代比例可能并不适配这一业务场景。默认设置下,JVM 可能分配较小的初始堆内存,这就好比一开始只给运动员提供少量能量,导致频繁触发垃圾回收。同时,如果新生代空间过小,对象可能很快就会被迫晋升到老年代,增加老年代的压力,引发频繁的 Full GC,如同让一个新手过早参加高强度比赛。我们可以通过调整 JVM 启动参数来量身定制内存配置。例如:
-Xms512m -Xmx1024m -XX:NewRatio=2
-Xms512m表示将 JVM 的初始堆内存精心设置为 512MB,这为系统启动时的对象分配提供了充足的弹药,有效减少了早期频繁扩展堆内存带来的性能损耗。-Xmx1024m则为 JVM 堆内存的增长划定了上限,防止其无节制扩张导致系统内存资源枯竭。-XX:NewRatio=2意味着新生代与老年代的内存比例被精准设定为 1:2,即新生代占据整个堆内存的 1/3,老年代占据 2/3。鉴于业务中短生命周期对象居多,适当增大新生代空间能够让更多的短期对象在新生代就被成功回收,减少对象晋升到老年代的几率,从而大幅降低 Full GC 的发生频率,显著提升系统的整体性能和响应速度,让系统如同换上了高性能引擎,运行更加顺畅高效。
优化效果:经过上述全方位、深层次的优化后,再次运用 VisualVM 对在线交易系统进行性能监控,效果堪称惊艳。从 CPU 使用率来看,原本因复杂算法和低效数据库查询导致的长期高位运行(如 80% - 90%),如今已大幅降低到合理区间(如 30% - 40%),CPU 得以从繁重的 “劳动” 中解脱出来。复杂计算方法的执行时间大幅缩短,例如订单总价计算时间从原来的几百毫秒锐减到几十毫秒,计算效率得到了质的飞跃。在内存方面,垃圾回收频率明显降低,通过 VisualVM 的内存监控图表可以清晰地看到,老年代内存占用趋于稳定,不再像优化前那样持续攀升,系统响应速度大幅提升,用户操作的平均响应时间从秒级缩短至毫秒级,极大地改善了用户体验,显著提升了系统的可用性和市场竞争力,使系统焕发出新的生机与活力。
四、JVM 在不同云环境及最新 Java 版本下的表现与优化
4.1 JVM 在主流云环境中的特性与优化要点
4.1.1 AWS(Amazon Web Services)
在 AWS 云环境中,JVM 的性能表现与底层的硬件资源配置以及 AWS 提供的云服务特性紧密相关。AWS 的弹性计算云(EC2)实例类型丰富多样,不同的实例类型在 CPU 性能、内存容量、网络带宽等方面存在显著差异。例如,计算优化型实例(如 C 系列)具备强大的 CPU 处理能力,适合运行 CPU 密集型的 Java 应用程序,对于这类应用,在配置 JVM 时,可以充分利用其多核 CPU 的优势,合理调整并行垃圾回收器的线程数,以提高垃圾回收的效率,减少 GC 停顿时间。同时,AWS 的 Elastic Block Store(EBS)提供了持久化存储,在处理大量磁盘 I/O 操作的 Java 应用中,要注意优化 JVM 的文件读写操作,充分利用 EBS 的性能特性,避免因频繁的磁盘 I/O 导致系统性能瓶颈。另外,AWS 的负载均衡服务(ELB)和自动扩展组(Auto Scaling)与 Java 应用的部署和伸缩密切相关。在使用这些服务时,要确保 JVM 的配置能够适应应用实例的动态增减,例如通过动态调整 JVM 的堆内存大小,以适应不同负载下的内存需求。
4.1.2 阿里云
阿里云作为国内领先的云计算平台,也为 JVM 的运行提供了独特的环境。阿里云的弹性计算服务(ECS)同样提供了多种实例规格,在选择实例类型时,需要根据 Java 应用的业务特点进行精准匹配。对于内存密集型的 Java 应用,优先选择内存优化型实例(如 r 系列),并相应地调整 JVM 的堆内存参数,增大堆内存的分配,以满足应用对大量内存的需求。阿里云的对象存储服务(OSS)常用于存储海量的非结构化数据,当 Java 应用与 OSS 进行交互时,要注意优化 JVM 的网络请求处理,合理设置网络连接池的参数,提高数据上传和下载的效率,减少网络延迟对应用性能的影响。此外,阿里云还提供了云监控服务(CloudMonitor),可以实时监测 Java 应用的运行状态,包括 JVM 的各项性能指标。通过与云监控服务的集成,开发者可以及时获取 JVM 的运行数据,根据监控数据动态调整 JVM 的配置,实现对应用性能的精细化管理。
4.2 结合最新 Java 版本(如 Java 24)的 JVM 特性改进
4.2.1 Java 24 的创新突破
Java 24 的发布为 Java 开发者带来了一系列令人振奋的改进,显著提升了 JVM 的性能与开发体验。模式匹配(Pattern Matching)在 Java 24 中得到进一步增强,在switch语句、instanceof检查等场景下,开发者能够以更为简洁、安全的方式进行类型判断和转换操作。以处理复杂对象层次结构为例,模式匹配允许代码直观地提取对象属性并执行相应处理,大幅减少了繁琐的类型检查代码,既提升了代码的可读性与可维护性,又有助于 JVM 在编译和运行时进行更精准的类型推断,优化字节码生成,从而显著提升程序的执行效率。
垃圾回收器在 Java 24 中也迎来了重大升级,引入了更为智能的内存管理策略。在应对大对象及长时间运行的应用程序时,垃圾回收器能够更高效地识别并回收不再使用的内存空间,有效减少内存碎片的产生,显著提升内存的整体利用率。这对于大数据分析、人工智能等需要处理海量数据的 Java 应用而言,能够极大地提升其性能与稳定性,确保系统在高负载下依然能够高效运行。
4.2.2 Java 24 对高并发及启动性能的优化
Java 24 在高并发处理方面取得了显著进展,虚拟线程(Virtual Threads)得到进一步完善与优化,成为构建高并发应用程序的有力工具。虚拟线程通过复用操作系统线程,在高并发场景下可创建数以万计的线程,却不会对系统资源造成沉重负担。与传统线程模型相比,虚拟线程大幅降低了线程创建和管理的开销,显著提升了应用程序的并发处理能力。在大规模网络服务器、分布式系统等实际应用场景中,虚拟线程能够充分发挥多核处理器的优势,实现更高效的并发任务处理,极大地提升系统的吞吐量和响应速度。
此外,Java 24 在 JVM 的启动性能上实现了重大突破。通过对类加载机制和资源初始化流程的优化,Java 应用程序的启动时间大幅缩短。对于那些需要频繁启动和停止的微服务架构应用来说,这一改进能够显著提高系统的部署和运维效率,降低资源消耗,使开发人员能够更快速地迭代和部署应用程序,为用户提供更为敏捷的服务体验。
结束语
通过对 JVM 关键知识点如垃圾回收机制、类加载器原理的深度钻研,以及熟练运用 JVM 性能监控工具进行优化实践,并且了解 JVM 在不同云环境中的特性和最新 Java 版本下的改进,我们不仅能有效攻克实际项目中的性能难题,还能为从容应对大厂面试中的相关考点做好充分准备。希望本文分享的宝贵经验与实用技巧,能助力各位技术爱好者在追求大厂 Offer 的征程中披荆斩棘,顺利迈出坚实有力的步伐。
亲爱的开源构架技术伙伴们!在运用 JVM 知识优化项目性能时,你遇到过哪些棘手难题?欢迎在评论区或架构师交流讨论区分享您的宝贵经验和见解,让我们一起共同探索这个充满无限可能的技术领域!
亲爱的开源构架技术伙伴们!最后到了投票环节:你认为 JVM 优化中,对系统性能提升最显著的措施是?投票直达。
- Java大厂面试高频考点|分布式系统JVM优化实战全解析(附真题)(New)
- Java大厂面试题 – JVM 优化进阶之路:从原理到实战的深度剖析(2)(New)
- Java大厂面试题 – 深度揭秘 JVM 优化:六道面试题与行业巨头实战解析(New)
- 开源架构与人工智能的融合:开启技术新纪元(New)
- 开源架构的自动化测试策略优化版(New)
- 开源架构的容器化部署优化版(New)
- 开源架构的微服务架构实践优化版(New)
- 开源架构中的数据库选择优化版(New)
- 开源架构的未来趋势优化版(New)
- 开源架构学习指南:文档与资源的智慧锦囊(New)
- 开源架构的社区贡献模式:铸就辉煌的创新之路(New)
- 开源架构与云计算的传奇融合(New)
- 开源架构:企业级应用的璀璨之星(New)
- 开源架构的性能优化:极致突破,引领卓越(New)
- 开源架构安全深度解析:挑战、措施与未来(New)
- 如何选择适合的开源架构框架(New)
- 开源架构与闭源架构:精彩对决与明智之选(New)
- 开源架构的优势(New)
- 常见的开源架构框架介绍(New)
- 开源架构的历史与发展(New)
- 开源架构入门指南(New)
- 开源架构师的非凡之旅:探索开源世界的魅力与无限可能(New)
🎯欢迎您投票
相关文章:
Java 大厂面试题 -- JVM 深度剖析:解锁大厂 Offe 的核心密钥
最近佳作推荐: Java大厂面试高频考点|分布式系统JVM优化实战全解析(附真题)(New) Java大厂面试题 – JVM 优化进阶之路:从原理到实战的深度剖析(2)(New&#…...
小白入门JVM、字节码、类加载机制图解
前提知识~ JDK 基本介绍 JDK 的全称(Java Development Kit Java 开发工具包)JDK JRE java 的开发工具[java, javac,javadoc,javap 等]JDK 是提供给Java 开发人员使用的,其中包含了java 的开发工具,也包括了JRE。可开发、编译、调试…… JRE 基本介绍…...
新能源汽车动力性与经济性优化中的经典数学模型
一、动力性优化数学模型 动力性优化的核心目标是提升车辆的加速性能、最高车速及爬坡能力,主要数学模型包括: 1. 车辆纵向动力学模型 模型方程: 应用场景: 计算不同工况下的驱动力需求匹配电机扭矩与减速器速比案例ÿ…...
高级java每日一道面试题-2025年3月25日-微服务篇[Nacos篇]-Nacos中的命名空间(Namespace)有什么作用?
如果有遗漏,评论区告诉我进行补充 面试官: Nacos中的命名空间(Namespace)有什么作用? 我回答: 在Java高级面试中,关于Nacos中的命名空间(Namespace)的作用,是一个考察候选人对微服务架构和配…...
5.JVM-G1垃圾回收器
一、什么是G1 二、G1的三种垃圾回收方式 region默认2048 三、YGC的过程(Step1) 3.1相关代码 public class YGC1 {/*-Xmx128M -XX:UseG1GC -XX:PrintGCTimeStamps -XX:PrintGCDetails -XX:UnlockExperimentalVMOptions -XX:G1LogLevelfinest128m5% 60%6.4M 75M*/private stati…...
变量、数据、值类型引用类型的存储方式
代码写了也有2年了,对于这些基础的程序名词,说出口也是模棱两可,心里很不爽,很多基础还是模糊不清,清算一下...... Example值类型: int x 10; 变量:“x”是一个标识符,它对应着栈…...
分布式和微服务的区别
1. 定义 在讨论分布式系统和微服务的区别之前,我们先明确两者的定义: 分布式系统:是一组相互独立的计算机,通过网络协同工作,共同完成某个任务的系统。其核心在于资源的分布和任务的分解。 微服务架构:是…...
数组的常见算法一
注: 本文来自尚硅谷-宋红康仅用来学习备份 6.1 数值型数组特征值统计 这里的特征值涉及到:平均值、最大值、最小值、总和等 **举例1:**数组统计:求总和、均值 public class TestArrayElementSum {public static void main(String[] args)…...
Leedcode刷题 | Day27_贪心算法01
一、学习任务 455.分发饼干代码随想录376. 摆动序列53. 最大子序和 二、具体题目 1.455分发饼干455. 分发饼干 - 力扣(LeetCode) 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对…...
springboot集成kafka,后续需要通过flask封装restful接口
Spring Boot与Kafka的整合 在现代软件开发中,消息队列是实现服务解耦、异步消息处理、流量削峰等场景的重要组件。Apache Kafka是一个分布式流处理平台,它具有高吞吐量、可扩展性和容错性等特点。Spring Boot作为一个轻量级的、用于构建微服务的框架&am…...
MYSQL数据库语法补充2
一,数据库设计范式(原则) 数据库设计三大范式: 第一范式: 保证列的原子性(列不可再分) 反例:联系方式(手机,邮箱,qq) 正例: 手机号,qq,邮箱. 第二范式: 要有主键,其他列依赖于主键列,因为主键是唯一的,依赖了主键,这行数据就是唯一的. 第三范式: 多表关联时,在…...
Flask返回文件方法详解
在 Flask 中返回文件可以通过 send_file 或 send_from_directory 方法实现。以下是详细方法和示例: 1. 使用 send_file 返回文件 这是最直接的方法,适用于返回任意路径的文件。 from flask import Flask, send_fileapp = Flask(__name__)@app.route("/download")…...
随机数据下的最短路问题(Dijstra优先队列)
题目描述 给定 NN 个点和 MM 条单向道路,每条道路都连接着两个点,每个点都有自己编号,分别为 1∼N1∼N 。 问你从 SS 点出发,到达每个点的最短路径为多少。 输入描述 输入第一行包含三个正整数 N,M,SN,M,S。 第 22 到 M1M1 行…...
CPP杂项
注意:声明类,只是告知有这个类未完全定义,若使用类里具体属性,要么将访问块(函数之类)放到定义后,要么直接完全定义 C 友元函数/类的使用关键点(声明顺序为核心) 1. 友元…...
Idea将Java工程打包成war包并发布
1、问题概述? 项目开发之后,我们需要将Java工程打包成war后缀,并进行发布。之前在网上看到很多的文章,但是都不齐全,今天将提供一个完整的实现打包war工程,并发布的文章,希望对大家有所帮助,主要解决如下问题: 1、war工程需要满足的相关配置 2、如何解决项目中的JDK…...
多级缓存模型设计
为了有效避免缓存击穿、穿透和雪崩的问题。最基本的缓存设计就是从数据库中查询数据时,无论数据库中是否存在数据,都会将查询的结果缓存起来,并设置一定的有效期。后续请求访问缓存时,如果缓存中存在指定Key时,哪怕对应…...
SGLang实战:从KV缓存复用到底层优化,解锁大模型高效推理的全栈方案
在当今快速发展的人工智能领域,大型语言模型(LLM)的应用已从简单对话扩展到需要复杂逻辑控制、多轮交互和结构化输出的高级任务。面对这一趋势,如何高效地微调并部署这些大模型成为开发者面临的核心挑战。本文将深入探讨SGLang——这一专为大模型设计的高…...
【中大厂面试题】腾讯 后端 校招 最新面试题
操作系统 进程和线程的区别 本质区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位 在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销&am…...
数据结构第六章(一) -图
数据结构第六章(一) 图一、图的基本概念1.图的分类、度、权等2.路径、回路、连通等3.连通分量、生成树等 二、几种特殊的图1.完全图2.稠密图、稀疏图3.树、有向树 三、常见考点总结 图 一、图的基本概念 感觉要想怎么定义图,图就是顶点和边组…...
【技术白皮书】外功心法 | 第三部分 | 数据结构与算法基础(常用的数据结构)
数据结构与算法基础 什么是算法?算法效率查询和排序为什么排序如此重要?思考问题如何确定复杂性数据结构连续或链接的数据结构链表的优点数组的优点数组集合Set 声明的一些方法有Multiset多元集合栈和队列何时使用栈和队列数据字典字典Hash的实现时间复杂度对时间的影响什么是…...
spark简介和安装
spark概念 Spark 是一种基于内存的快速、通用、可扩展的大数据分析计算引擎 spark核心模块 Spark Core Spark Core 中提供了 Spark 最基础与最核心的功能,Spark 其他的功能如:Spark SQL,Spark Streaming,GraphX, MLlib 都是在 …...
如何在 CentOS 7 系统上以容器方式部署 GitLab,使用 ZeroNews 通过互联网访问 GitLab 私有仓库,进行代码版本发布与更新
第 1 步: 部署 GitLab 容器 在开始部署 GitLab 容器之前,您需要创建本地目录来存储 GitLab 数据、配置和日志: #创建本地目录 mkdir -p /opt/docker/gitlab/data mkdir -p /opt/docker/gitlab/config mkdir -p /opt/docker/gitlab/log#gi…...
springboot Filter实现请求响应全链路拦截!完整日志监控方案
一、为什么你需要这个过滤器? 日志痛点: 🚨 请求参数散落在各处? 🚨 响应数据无法统一记录? 🚨 日志与业务代码严重耦合? 解决方案: 一个Filter同时拦截请…...
spring mvc 在拦截器、控制器和视图中获取和使用国际化区域信息的完整示例
在拦截器、控制器和视图中获取和使用国际化区域信息的完整示例 1. 核心组件代码示例 1.1 配置类(Spring Boot) import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.spring…...
docker部署jenkins并成功自动化部署微服务
一、环境版本清单: docker 26.1.4JDK 17.0.28Mysql 8.0.27Redis 6.0.5nacos 2.5.1maven 3.8.8jenkins 2.492.2 二、服务架构:有gateway,archives,system这三个服务 三、部署步骤 四、安装linux 五、在linux上安装redis&#…...
android 14.0 工厂模式 测试音频的一些问题(高通)
1之前用tinycap,现在得用agmcap 执行----agmcap /data/test.wav -D 100 -d 101 -i CODEC_DMA-LPAIF_RXTX-TX-3 -T 3 报错1 agmcap data/test.wav -D 100 -d 101 -i CODEC_DMA-LPAIF_RXTX-TX-3 -T 3 Failed to open xml file name /vendor/etc/backend_co…...
vue:前端预览 / chrome浏览器设置 / <iframe> 方法预览 doc、pdf / vue-pdf 预览pdf
一、本文目标 <iframe> 方法预览 pdf 、word vue-pdf 预览pdf 二、<iframe> 方法 2.1、iframe 方法预览需要 浏览器 设置为: chrome:设置-隐私设置和安全性-网站设置-更多内容设置-PDF文档 浏览器访问: chrome://settings/co…...
Axure疑难杂症:详解横向菜单左右拖动效果及阈值说明
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:横向菜单左右拖动 主要内容:菜单设计、拖动效果、阈值设计 应用场景:APP横向菜单栏、台账菜单栏、功能选择栏等 案例展示: 案例视频: 横向菜单左右拖动效果 正文内容: 最近很多粉丝私信我,横向…...
多视图几何--立体校正--Bouguet方法
Bouguet算法的数学原理详解 Bouguet算法的核心目标是实现双目相机的极线校正,使左右图像的对应点位于同一水平线上,从而简化立体匹配。其数学原理围绕旋转矩阵分解、极线约束构造和重投影优化展开,以下是分步推导: 一、坐标系定义…...
debian12 mysql完全卸载
MySQL 的数据目录通常是 /var/lib/mysql,配置文件通常在 /etc/mysql 目录下。使用以下命令删除这些目录: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql清理残留文件 sudo find / -name "mysql*" -exec rm -rf {} \; 2>/dev/null验…...
随机动作指令活体检测技术的广泛应用,为人脸识别安全保驾护航
随着人脸识别技术在金融支付、门禁系统、手机解锁等领域的广泛应用,攻击手段也日益多样化,如照片、视频回放、3D面具等伪造方式对系统安全构成严重威胁。传统的人脸识别技术难以区分真实人脸与伪造攻击,正是在这样的背景下,随机动…...
Chrome 135 版本开发者工具(DevTools)更新内容
Chrome 135 版本开发者工具(DevTools)更新内容 一、性能(Performance)面板改进 1. 性能面板中的配置文件和函数调用现已显示来源和脚本链接 Performance > Summary(性能 > 概览)选项卡现在会显示配…...
《算法:以三种算法思想及两种优化策略解决Fibonacci数》
文章目录 0.题目:red_circle:一.递归记忆化搜索**a.普通递归****b.记忆化搜索(优化)**算法讲解: :red_circle:二.前缀和a.算法讲解b.代码示例 :red_circle:三.动态规划滚动数组a.算法讲解b.普通动规代码示例c.滚动数组优化 作者的个人gitee 作者的算法讲…...
定制开发开源AI智能名片S2B2C商城小程序源码中的产品运营协同进化机制研究
摘要 在数字化产品发展进程中,产品经理与运营人员的协同工作机制对产品迭代方向具有决定性作用。本文以定制开发开源AI智能名片S2B2C商城小程序源码为研究对象,通过分析其技术架构中的智能推荐系统、三级账户体系及社交裂变引擎等创新模块,揭…...
Colmap的安装和使用
Colmap 网站: https://colmap.github.io/GitHub: https://github.com/colmap/colmap 安装 Windows 从 GitHub发布页 GitHub Releases 下载预编译的二进制, 区分带CUDA和不带CUDA的版本. Ubuntu 在 Ubuntu 22.04 下可以通过apt install colmap安装, 但是这样安装的是不带C…...
Springboot框架—单元测试操作
Springboot单元测试的操作步骤: 1.添加依赖spring-boot-starter-test 在pom.xml中添加依赖spring-boot-starter-test 2.在src/test/java下新建java class 3.单元测试入口代码结构 import org.junit.Test; import org.junit.runner.RunWith; import org.springfra…...
Elasticearch数据流向
Elasticearch数据流向 数据流向图 --- config: layout: elk look: classic theme: mc --- flowchart LR subgraph s1["图例"] direction TB W["写入流程"] R["读取流程"] end A["Logstash Pipeline"] -- 写入请求 --> B["Elas…...
dB,dBi, dBd, dBc,dBm,dBw释义及区别
贝尔(B) 贝尔(B)最初用于表示音量功率10与1的比值,亚历山大.格拉汉姆.贝尔的名字命名。因此,1B表示功率比10:1,这是一种对数的关系,底数为10,100:12B,1000:13B。即lg(P2/P1)。 可以看出B是一个较大的单位…...
CRM是企业AI Agent应用的关键切入点
CRM(Customer Relationship Management,客户关系管理)是一套用于系统化管理企业与客户交互的技术、策略和流程,旨在优化客户体验、提升忠诚度并驱动业务增长。其核心功能包括客户数据整合(如联系信息、交易记录、行为轨…...
aws(学习笔记第三十八课) codepipeline-build-deploy-github-manual
文章目录 aws(学习笔记第三十八课) codepipeline-build-deploy-github-manual学习内容:1. 整体架构1.1 代码链接1.2 全体处理架构 2. 代码分析2.1 创建ImageRepo,并设定给FargateTaskDef2.2 创建CodeBuild project2.3 对CodeBuild project赋予权限&#…...
【数据分享】1999—2023年地级市市政公用事业和邮政、电信业发展情况相关指标(Shp/Excel格式)
在之前的文章中,我们分享过基于2000-2024年《中国城市统计年鉴》整理的1999-2023年地级市的人口相关数据、染物排放和环境治理相关数据、房地产投资情况和商品房销售面积相关指标数据、社会消费品零售总额和年末金融机构存贷款余额、各类用地面积、地方一般公共预算…...
vscode 跳转失败之c_cpp_properties.json解析
{"configurations": [{"name": "Linux", // 配置名称,对应当前平台,VS Code 中可选"includePath": ["${workspaceFolder}/**", // 包含当前工作区下所有文件夹的头文件(递归&…...
批量将图片转换为 jpg/png/Word/PDF/Excel 等其它格式
图片的格式种类非常的多,常见的图片格式就有 jpg、png、webp、bmp 等等,我们在工作当中经常会碰到需要将一种格式的图片转换为其他格式的需求。今天就给大家介绍一种方法,可以实现批量转换图片格式,支持各种格式的图片之间相互转换…...
MCP+Blender创建电力塔
MCP(Model Context Protocol)与Blender的结合是当前AI与3D建模领域的热门技术,它通过协议化的方式让Claude等AI模型直接控制Blender,实现自动化3D建模。 1. 功能与原理 • 核心能力:用户通过自然语言指令(…...
kafka存储原理
topic分着存储在broker的分区中,分区进一步分为segment。 日志目录中的每一组文件都代表一个段。 段文件名中的后缀表示该段的基本偏移量。 log.segment.bytes表示分段的最大大小。 消息写入分区时,kafka会将这些消息写入段,写满了再创建一个…...
Arduino开发物联网ESP32快速入门指南(包含开发语言说明、学习路径和实战教程)
一、Arduino开发语言本质 核心语言:基于C/C的扩展语言,包含Arduino特有的API和库 特点: 去除了C的复杂特性(如STL) 内置硬件操作函数(digitalWrite()、analogRead()等) 支持面向对象编程&…...
《系统架构设计师教程(第2版)》第19章-大数据架构设计理论与实践-01-传统数据处理系统存在的问题
文章目录 1. 异步处理队列2. 数据库分区3 读写分离4. 基于Hadoop的 Map/Reduce 管道 1. 异步处理队列 出现的原因:用户访问量增加,数据库无法支撑用户请求的负载,导致数据库服务器无法及时响应用户请求解决:在Web服务器和数据库中…...
Vue接口平台学习五——测试环境页面
一、实现效果图及界面布局简单梳理 这块内容分左侧,中间,右侧三大部分 左侧: 上面一行固定内容,显示icon,名字,一个按钮。下面部分通过v-for循环读取数据库获取的测试环境列表用来展示名称。 中间&#…...
FFMpeg视频编码实战和音频编码实战
视频编码流程 avcodec_find_encoder:首先,通过指定的编码器名称(如H.264、MPEG-4等)找到对应的编码器。avcodec_alloc_context3:为找到的编码器分配一个上下文结构,这个结构包含了编码器所需的各种参数和状…...
vue+uniapp 获取上一页直接传递的参数
在小程序里页面之间跳转有时候需要传递参数给下个页面用 const toDetail item > { uni.navigateTo({ url: /pagesFood/stu/FoodSelection?groupCode1&merchCode2, }); }; 那么下个页面就要获取到这些参数,在实际开发中ÿ…...