文章目录

    • 一、前言
      • 1.1 什么是JVM内存结构
      • 1.2 JVM内存结构与Java内存模型的区别
      • 1.3 为什么面试官爱问JVM内存结构
    • 二、JVM运行时数据区总览
      • 2.1 运行时数据区域划分
      • 2.2 线程私有区域 vs 线程共享区域
    • 三、线程私有区域详解
      • 3.1 程序计数器(PC Register)
        • 3.1.1 定义与作用
        • 3.1.2 多线程环境下的工作原理
        • 3.1.3 为什么不会发生OOM
      • 3.2 Java虚拟机栈(JVM Stack)
        • 3.2.1 栈的基本概念
        • 3.2.2 栈帧结构详解
        • 3.2.3 局部变量表
        • 3.2.4 操作数栈
        • 3.2.5 动态链接
        • 3.2.6 方法返回地址
        • 3.2.7 栈相关异常
      • 3.3 本地方法栈(Native Method Stack)
        • 3.3.1 本地方法接口
        • 3.3.2 本地方法栈的内存结构
        • 3.3.3 与虚拟机栈的区别与联系

一、前言

1.1 什么是JVM内存结构

JVM内存结构是指Java虚拟机在运行时对内存空间的划分和管理方式。它定义了JVM如何组织和使用内存来存储程序运行时的各种数据,包括类信息、对象实例、方法调用等。

JVM内存结构是Java程序能够实现"一次编写,到处运行"的核心基础,也是理解Java程序性能优化、内存泄漏排查、垃圾回收机制的关键。

1.2 JVM内存结构与Java内存模型的区别

这是一个经常被混淆的概念:

  • JVM内存结构:描述的是JVM运行时内存的物理布局,包括堆、栈、方法区等区域的划分
  • Java内存模型(JMM):描述的是多线程环境下共享变量的访问规则,主要解决可见性、原子性、有序性问题

简单记忆:内存结构看"空间",内存模型看"规则"。

1.3 为什么面试官爱问JVM内存结构

  1. 基础性强:是理解JVM工作原理的入门知识
  2. 实用性高:直接关系到程序性能和故障排查
  3. 区分度好:能有效区分候选人的技术深度
  4. 延展性强:可以引出GC、调优、并发等高级话题

二、JVM运行时数据区总览

2.1 运行时数据区域划分

根据《Java虚拟机规范》,JVM运行时数据区包含以下几个部分:

JVM运行时数据区
线程私有区域
线程共享区域
直接内存
程序计数器
Program Counter
Java虚拟机栈
JVM Stack
本地方法栈
Native Method Stack
Java堆
Heap
方法区
Method Area
新生代
Young Generation
老年代
Old Generation
Eden区
Survivor 0
Survivor 1
运行时常量池
Runtime Constant Pool
类元数据
Class Metadata

图解说明

  • 蓝色区域:线程私有,每个线程独享
  • 紫色区域:线程共享,所有线程共用
  • 橙色区域:直接内存,不属于JVM规范定义的内存区域

2.2 线程私有区域 vs 线程共享区域

特性线程私有区域线程共享区域
访问权限只能被创建它的线程访问所有线程都可以访问
生命周期与线程同生共死与JVM进程同生共死
线程安全天然线程安全需要考虑线程安全
垃圾回收一般不需要GC需要GC管理
包含区域程序计数器、虚拟机栈、本地方法栈堆、方法区

三、线程私有区域详解

3.1 程序计数器(PC Register)

3.1.1 定义与作用

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。

主要作用

  1. 记录执行位置:存储当前线程正在执行的字节码指令的地址
  2. 支持线程切换:保证线程切换后能恢复到正确的执行位置
  3. 分支控制:支持循环、跳转、异常处理等控制流
3.1.2 多线程环境下的工作原理
CPU线程1线程2PC寄存器1PC寄存器2执行线程1执行指令更新PC值为100线程切换切换到线程2从PC值50继续执行再次切换切换回线程1从PC值100继续执行CPU线程1线程2PC寄存器1PC寄存器2

图解说明:每个线程都有独立的程序计数器,保证了多线程环境下各线程互不干扰,能正确恢复执行位置。

3.1.3 为什么不会发生OOM

程序计数器是唯一不会发生OutOfMemoryError的内存区域,原因:

  1. 固定大小:只存储一个指令地址,占用空间极小且固定
  2. 简单数据:只存储基本的数字地址,不存储复杂对象
  3. 自动管理:随着指令执行自动更新,无需手动分配和释放

3.2 Java虚拟机栈(JVM Stack)

3.2.1 栈的基本概念

Java虚拟机栈是线程私有的,生命周期与线程相同。每个方法执行时都会创建一个栈帧,用于存储方法的局部变量、操作数、动态链接和返回地址等信息。

Java虚拟机栈
栈帧3 - method3
栈帧2 - method2
栈帧1 - method1
栈底
局部变量表
操作数栈
动态链接
方法返回地址

图解说明

  • 栈是LIFO(后进先出)结构
  • 每个方法调用对应一个栈帧
  • 当前执行的方法对应栈顶的栈帧
3.2.2 栈帧结构详解

栈帧是方法调用过程中的数据结构,包含四个主要组成部分:

  1. 局部变量表(Local Variable Table):存储方法参数和局部变量
  2. 操作数栈(Operand Stack):存储方法执行过程中的操作数
  3. 动态链接(Dynamic Linking):指向运行时常量池的方法引用
  4. 方法返回地址(Return Address):存储方法返回时的位置信息

这四个部分协同工作,确保了方法调用的正确执行。在编译期,局部变量表大小和操作数栈深度就已经确定,而动态链接和返回地址则在运行时确定。接下来我们将详细介绍每个组成部分的具体功能和工作原理。

3.2.3 局部变量表

局部变量表是栈帧中最重要的组成部分,用于存储方法参数和方法内定义的局部变量。它是一个有序的变量数组,在编译期就确定了大小,运行期不会改变。

存储结构与容量

局部变量表以变量槽(Variable Slot)为最小单位进行存储:

  • 槽的大小:每个槽可以存放一个32位以内的数据类型
  • 双槽类型:64位的数据类型(long和double)需要占用两个连续的槽
  • 容量确定:在编译期通过代码分析确定所需的最大槽数量,存储在方法的Code属性的max_locals数据项中

变量存储规则

局部变量表按照固定的顺序存储变量:

public void example(int param1, long param2, String param3) {int local1 = 10;double local2 = 3.14;Object local3 = new Object();// 局部变量表槽位分配:// 槽 0: this (实例方法的隐式参数)// 槽 1: param1 (int,占用1个槽)// 槽 2-3: param2 (long,占用2个槽)// 槽 4: param3 (String引用,占用1个槽)// 槽 5: local1 (int,占用1个槽)// 槽 6-7: local2 (double,占用2个槽)// 槽 8: local3 (Object引用,占用1个槽)
}

槽复用机制

为了节省栈帧空间,局部变量表允许槽的复用:

public void slotReuse() {{int a = 1;  // 占用槽1int b = 2;  // 占用槽2} // a和b超出作用域{int c = 3;  // 可以复用槽1int d = 4;  // 可以复用槽2}
}

this引用的特殊处理

对于实例方法,局部变量表的第0个槽永远存储当前对象的this引用:

  • 实例方法:第0个槽存储this,从第1个槽开始存储方法参数
  • 静态方法:没有this引用,直接从第0个槽开始存储方法参数
  • this的用途:用于访问实例变量、调用其他实例方法等

数据类型与槽的对应关系

数据类型占用槽数说明
boolean, byte, char, short, int132位及以下类型
float132位浮点数
long264位长整型
double264位双精度浮点数
reference1对象引用

局部变量表与垃圾回收

局部变量表中的变量引用会影响垃圾回收:

public void gcExample() {{byte[] placeholder = new byte[64 * 1024 * 1024]; // 64MB数组System.gc(); // 此时数组不会被回收,因为placeholder还在局部变量表中}// placeholder超出作用域,但槽可能还没被复用System.gc(); // 数组仍可能不被回收int a = 0; // 这个赋值可能复用了placeholder的槽System.gc(); // 现在数组可以被回收了
}

性能影响

局部变量表的设计对性能有重要影响:

  • 访问效率:通过索引直接访问,效率很高
  • 槽复用:减少栈帧大小,节省内存
  • 编译优化:编译器会优化局部变量的分配,减少不必要的槽使用

通过合理的槽分配和复用机制,局部变量表在保证程序正确性的同时,也最大化了内存使用效率。

3.2.4 操作数栈

操作数栈是栈帧中用于存储计算过程中操作数的数据结构,它是一个后进先出(LIFO)的栈。在方法执行过程中,各种字节码指令会向操作数栈写入和提取内容,完成各种算术运算和逻辑操作。

基本特性

  • 栈结构:严格遵循LIFO原则,只能在栈顶进行push和pop操作
  • 编译期确定:栈的最大深度在编译期就已确定,存储在方法的Code属性的max_stacks数据项中
  • 类型安全:虚拟机会验证操作数栈上的数据类型与指令要求是否匹配
  • 动态使用:在方法执行过程中,栈的实际深度会不断变化

操作数栈的工作原理

通过一个简单的加法运算来理解操作数栈的工作过程:

public int add(int a, int b) {return a + b;
}

对应的字节码和操作数栈变化:

字节码: iload_1
加载局部变量a
操作数栈
栈顶: a的值
栈底: 空
字节码: iload_2
加载局部变量b
操作数栈
栈顶: b的值
次栈顶: a的值
栈底: 空
字节码: iadd
整数加法
操作数栈
栈顶: a+b的结果
栈底: 空
字节码: ireturn
返回整数值

常见字节码指令与操作数栈的交互

  1. 加载指令(load)

    • iload_n:将局部变量表索引n的int值压入栈顶
    • aload_0:将局部变量表索引0的引用压入栈顶(通常是this)
  2. 存储指令(store)

    • istore_n:将栈顶int值存储到局部变量表索引n
    • astore_n:将栈顶引用值存储到局部变量表索引n
  3. 运算指令

    • iadd:弹出栈顶两个int值,相加后压入栈顶
    • imul:弹出栈顶两个int值,相乘后压入栈顶
  4. 类型转换指令

    • i2l:将栈顶int值转换为long值

复杂表达式的栈操作示例

public int calculate(int x, int y, int z) {return (x + y) * z;
}

执行过程中操作数栈的变化:

步骤字节码指令操作数栈状态说明
1iload_1[x]加载参数x
2iload_2[x, y]加载参数y
3iadd[x+y]计算x+y
4iload_3[x+y, z]加载参数z
5imul[(x+y)*z]计算最终结果
6ireturn[]返回结果,栈清空

操作数栈的优化

现代JVM对操作数栈进行了多种优化:

  1. 栈顶缓存优化:将栈顶元素直接映射到物理CPU的寄存器中
  2. 栈合并优化:将相邻方法的操作数栈和局部变量表进行重叠,减少数据复制
  3. 指令合并:将多个简单指令合并为一个复合指令

操作数栈与方法调用

在方法调用过程中,操作数栈还负责参数传递:

public void caller() {int result = add(10, 20);  // 调用add方法
}public int add(int a, int b) {return a + b;
}

调用过程:

  1. 参数准备:调用者将参数10和20压入操作数栈
  2. 方法调用:执行invoke指令,参数从调用者的操作数栈传递到被调用者的局部变量表
  3. 结果返回:被调用者将返回值压入调用者的操作数栈

栈溢出与异常处理

操作数栈相关的异常情况:

  • 验证错误:如果指令要求的操作数类型与栈上实际类型不匹配
  • 栈深度超限:理论上可能发生,但实际上由编译器保证不会出现
  • 栈空异常:试图从空栈弹出元素时发生

通过这种精妙的栈机制,JVM能够高效地执行各种复杂的计算操作,同时保证类型安全和执行正确性。

3.2.5 动态链接

动态链接是栈帧中指向运行时常量池的引用,它负责将字节码中的符号引用转换为直接引用,是实现Java语言多态性的重要机制。每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用。

符号引用与直接引用

在Java编译过程中,所有的变量和方法调用都是以符号引用的形式存储在Class文件的常量池中:

  • 符号引用:以一组符号描述所引用的目标,与虚拟机实现的内存布局无关
  • 直接引用:直接指向目标的指针、相对偏移量或能间接定位到目标的句柄
public class Example {public void methodA() {methodB();  // 编译时生成对methodB的符号引用}public void methodB() {System.out.println("Hello");  // 对System.out.println的符号引用}
}

解析过程与时机

符号引用的解析可以发生在不同时机:

  1. 静态解析(早期绑定)
    • 发生时机:类加载阶段的解析过程
    • 适用场景:静态方法、私有方法、实例构造器、父类方法
    • 特点:编译期就能确定唯一的调用版本
public class StaticBinding {private void privateMethod() { }     // 私有方法 - 静态解析static void staticMethod() { }       // 静态方法 - 静态解析final void finalMethod() { }         // final方法 - 静态解析
}
  1. 动态解析(晚期绑定)
    • 发生时机:方法第一次调用时
    • 适用场景:虚方法调用、接口方法调用
    • 特点:运行期根据实际类型确定调用版本
public class DynamicBinding {public void virtualMethod() { }      // 虚方法 - 动态解析
}class Child extends DynamicBinding {@Overridepublic void virtualMethod() {        // 重写方法 - 运行时确定调用版本// 具体实现}
}

方法调用指令与解析

不同的方法调用字节码指令采用不同的解析策略:

指令调用类型解析时机示例
invokestatic静态方法调用静态解析Math.max(a, b)
invokespecial特殊方法调用静态解析super.method(), this()
invokevirtual虚方法调用动态解析obj.method()
invokeinterface接口方法调用动态解析list.add(item)
invokedynamic动态方法调用动态解析Lambda表达式、方法句柄

多态的实现机制

动态链接是实现Java多态的核心机制:

public class PolymorphismExample {public static void main(String[] args) {Animal animal1 = new Dog();Animal animal2 = new Cat();animal1.makeSound();  // 运行时确定调用Dog.makeSound()animal2.makeSound();  // 运行时确定调用Cat.makeSound()}
}abstract class Animal {public abstract void makeSound();
}class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("Woof!");}
}class Cat extends Animal {@Overridepublic void makeSound() {System.out.println("Meow!");}
}

虚方法调用的解析过程:

  1. 查找方法:从对象的实际类型开始查找方法
  2. 向上搜索:如果当前类没找到,向父类继续搜索
  3. 权限检查:确认调用者是否有权限访问找到的方法
  4. 调用执行:执行找到的目标方法

方法区的配合

动态链接与方法区密切配合工作:

栈帧
动态链接
运行时常量池
符号引用
解析过程
直接引用
方法区中的方法信息

性能优化

为了提高动态链接的性能,JVM采用了多种优化策略:

  1. 内联缓存(Inline Cache):缓存最近解析的方法调用结果
  2. 方法内联:将频繁调用的小方法直接嵌入到调用者中
  3. 去虚拟化:对于只有一个实现的接口方法,直接调用实现类方法

invokedynamic指令

JDK 7引入的invokedynamic指令为动态语言提供了更灵活的方法调用机制:

// Lambda表达式使用invokedynamic
List<String> list = Arrays.asList("a", "b", "c");
list.forEach(s -> System.out.println(s));  // 编译后使用invokedynamic

动态链接机制确保了Java的面向对象特性能够正确实现,同时也是Java平台支持多种编程范式的基础。通过符号引用的动态解析,Java程序能够在运行时灵活地确定方法调用的目标,实现了真正的动态绑定。

3.2.6 方法返回地址

方法返回地址存储方法退出后的返回位置:

  • 正常退出:返回调用者的下一条指令地址
  • 异常退出:通过异常处理器表确定返回地址
3.2.7 栈相关异常
虚拟机栈异常
StackOverflowError
OutOfMemoryError
线程请求的栈深度 > 最大深度
常见场景:递归调用过深
动态扩展栈时内存不足
常见场景:创建线程过多

异常示例

// StackOverflowError示例
public void recursiveMethod() {recursiveMethod(); // 无限递归,最终导致栈溢出
}

3.3 本地方法栈(Native Method Stack)

本地方法栈是JVM为执行本地方法(Native Method)提供服务的内存区域。它与Java虚拟机栈类似,但专门用于支持用其他编程语言(如C、C++、汇编等)编写的本地方法的执行。

3.3.1 本地方法接口

什么是Native方法

Native方法是指使用native关键字修饰的,由其他编程语言实现的方法。这些方法允许Java程序调用本地系统库或执行特定平台的操作。

JNI(Java Native Interface)

JNI是Java平台的一部分,它提供了Java代码与本地代码之间的桥梁:

img

JNI的主要功能

  • 数据类型映射:Java基本类型与C/C++类型之间的转换
  • 对象访问:访问Java对象的字段和方法
  • 异常处理:在本地代码中处理Java异常
  • 内存管理:管理Java堆内存的访问
  • 线程同步:支持多线程环境下的同步操作
3.3.2 本地方法栈的内存结构

栈结构特点

本地方法栈的结构与Java虚拟机栈相似,但存储的内容不同:

本地方法栈
Native栈帧1
Native栈帧2
Native栈帧3
局部变量区
参数区
返回地址
JNI环境指针
C/C++局部变量
Java传入的参数
Java方法返回地址
JNIEnv指针
3.3.3 与虚拟机栈的区别与联系
特性Java虚拟机栈本地方法栈
服务对象Java方法Native方法
实现语言JVM规范定义通常用C/C++实现
在HotSpot中与本地方法栈合并与虚拟机栈合并
异常类型StackOverflowError、OOM同虚拟机栈
栈帧内容局部变量表、操作数栈、动态链接、返回地址本地变量、参数、返回地址、JNI环境

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

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

相关文章

鸿蒙中使用极光推送

官方给出的步骤是对的&#xff0c;就是一时不知道从何下手&#xff0c;自己整了下&#xff0c;按照这个来就行 1.步骤 打开 APP 通知功能 1.先按照这个页面进行配置SDK 集成指南 - 极光文档&#xff0c;主要就是下载极光sdk&#xff0c;然后在AGC里开通推送服务&#xff0c;配…

ruoyi_wvp流媒体[海康 大华 GB1812 onvif rtsp]

ZLMediaKitxiaz: https://download.csdn.net/download/jinhuding/91775096 webrtc: https://download.csdn.net/download/jinhuding/91764243 yoloonnx(v3,v7,v8s,v9c)&#xff1a;https://download.csdn.net/download/jinhuding/91775170 项目部署步骤 1.后端目录结构 2.前端…

强化学习笔记(二):有限马尔可夫决策过程(一)

有限马尔可夫决策过程 基本概念 多臂老虎机仅涉及评价性反馈&#xff0c;即动作的即时奖励&#xff0c;估计每个动作 aaa 的价值 q∗(a)q_*(a)q∗​(a)。 有限马尔可夫决策过程&#xff08;Finite MDP&#xff09;引入了关联性因素&#xff0c;即在不同状态&#xff08;情境&am…

Maven项目中settings.xml终极优化指南

文章目录1. 基础优化2. 镜像源优化&#xff08;国内推荐&#xff09;3. 插件仓库优化4. 并行构建提升 30%-80%5. 下载可靠性优化6. CI/CD 环境优化7. 进阶&#xff1a;依赖锁定与预下载8. 实现效果Maven settings.xml 终极优化指南&#xff0c;重点是&#xff1a;构建速度提升、…

RCC_APB2PeriphClockCmd

RCC_APB2PeriphClockCmd 函数在STM32的标准外设库中扮演着“电源开关”的角色。要理解这个函数&#xff0c;我们需要明白STM32微控制器的几个关键概念&#xff1a;1. 外设时钟与低功耗设计STM32内部有非常多的外设&#xff0c;如GPIO&#xff08;A, B, C...D&#xff09;、USAR…

用大语言模型实现语音到语音翻译的新方法:Scheduled Interleaved Speech-Text Training

用大语言模型实现语音到语音翻译的新方法:Scheduled Interleaved Speech-Text Training 在人工智能领域,语音到语音翻译(Speech-to-Speech Translation, S2ST)一直是极具挑战性的任务。传统的做法是将语音识别、文本翻译和语音合成三个步骤串联起来,而近年来,端到端的S2…

LLM学习:langchain架构——模型IO

1、什么是模型IO模型 I/O&#xff08;Model I/O&#xff09; 是 LangChain 框架中最核心的模块之一&#xff0c;负责处理与语言模型&#xff08;LLM&#xff09;交互的输入构建、模型调用和输出解析全流程。它主要分为三个模块&#xff1a;Prompts&#xff08;输入构建&#xf…

Windows系统下python新一代三方库管理工具uv及VSCode配置

python新一代三方库管理工具uv uv是什么&#xff1f; uv是用RUST语言写的一个python三方库和项目管理工具&#xff0c;详见官网&#xff08;uv&#xff09;。 uv的安装 官网上提供了两种安装方式&#xff0c;第一种需要在PS终端里运行一下命令进行安装&#xff1a; powersh…

Node.js 多版本管理工具 nvm 的安装与使用教程(含镜像加速与常见坑)

适用人群&#xff1a;前端/后端/全栈开发者&#xff0c;Mac/Linux/Windows&#xff08;nvm-windows&#xff09;用户&#xff1b;需要在多项目间快速切换 Node 版本、或在国内网络环境下稳定安装 Node。一、为什么要用 nvm&#xff1f;一机多版本&#xff1a;不同项目依赖不同 …

Unity Shader unity文档学习笔记(二十一):几种草体的实现方式(透明度剔除,GPU Instaning, 曲面细分+几何着色器实现)

1.透明度剔除&#xff08;性能较差&#xff0c;不同颜色时需要不同材质会导致多个dc&#xff09; clip(_Color.a - _Cutoff); 传入值为0时 剔除 类似的草体效果&#xff1a; 2.GPU Instaning(可以自定义一次性合批最多1023个&#xff0c;能够传递颜色值等等&#xff08;做草…

UX 设计入门终章:让洞察落地!用用户流程图、IA 和旅程图,设计用户与产品的互动故事

欢迎来到本系列课程的最后一课。 如果你把之前的学习比作是绘制一份建筑蓝图&#xff0c;那么今天&#xff0c;你将根据自己收集到的所有用户数据&#xff0c;描绘出空间布局&#xff08;用户流程图&#xff09;、理清结构关系&#xff08;信息架构&#xff09;&#xff0c;并最…

【RAG知识库实践】向量数据库VectorDB

一、概述 1.1 什么是向量库 向量数据库是一种专门为存储、索引和查询高维向量数据而优化的数据库系统。与传统的关系型数据库不同,向量数据库将数据映射到向量空间中,使得数据的相似性计算、聚类、分类和检索变得更加高效和精确 向量数据库一般包括以下几个部分:索引、查询…

EasyExcel 3.x 导出动态表头,动态sheet页

动态导出sheet页Overridepublic void exportAnswerListV1(HttpServletResponse response, SmtSurveyUserAnswerRecord smtSurveyUserAnswerRecord) {// 1. 准备问卷数据String formType smtSurveyUserAnswerRecord.getFormType();if (ObjectUtil.isEmpty(formType)) {throw ne…

重学JS-004 --- JavaScript算法与数据结构(四)JavaScript 表单验证

文章目录HTMLlabel 属性input 属性button 属性fieldset 属性select 属性option 属性div 属性scriptgetElementByIdquerySelectorAllnull循环模版文字函数事件监听器regex举例StringMathArrayHTML HTML 属性应该用双引号引起来。 label 属性 for“” input 属性 id“” typ…

本地搭建 Redis/MySQL 并配置国内镜像加速(Docker/原生安装 | macOS/Linux/Windows)

适用人群&#xff1a;前端/后端/数据/测试工程师&#xff1b;需要在单机上快速搭建 Redis 与 MySQL 的开发环境&#xff1b;同时在国内网络环境下加速下载&#xff08;容器镜像、系统包仓库&#xff09;。文章结构&#xff1a;一图流 → TL;DR → Docker 方式 → 原生安装&…

SynClub-百度在海外推出的AI社交产品

本文转载自&#xff1a;SynClub-百度在海外推出的AI社交产品 - Hello123工具导航 ** 一、&#x1f916; SynClub 是什么&#xff1f; SynClub 是百度出海的 AI 社交产品&#xff0c;主打 "打造专属 AI 朋友" 的创新概念&#xff0c;让你在虚拟世界拥有真正懂你的贴…

Easy Rules规则引擎:轻量级Java规则处理实践指南

引言:业务规则与代码解耦的艺术 在现代软件开发中,业务规则频繁变更与核心逻辑稳定性之间的矛盾日益突出。传统硬编码的if-else语句在面对复杂业务场景时会导致: 代码臃肿:成百上千的条件分支难以维护 耦合度高:业务逻辑与系统架构深度绑定 变更成本高:微小规则调整需要…

机器人 - 无人机基础(5) - 飞控中的传感器

目录 一、加速度计 1.1 加速度计原理 1.2 加速度计校准 1.2.1 误差模型 1.2.2 关于MPU6050校准方式 与 代码思路 二、陀螺仪 2.1 陀螺仪原理 2.1.1 科里奥利力 2.1.2 陀螺仪原理 2.2 陀螺仪校准 三、加速度计与陀螺仪滤波部分 四、磁力计 4.1 磁力计原理 4.2 磁力…

PHP - 线程安全 - 疑问与答案

线程安全问题&#xff08;多线程环境需加锁&#xff09; 怎么理解 php有线程安全问题吗要理解 PHP 中的线程安全问题&#xff0c;需要结合 PHP 的运行模式和线程安全的本质来分析&#xff1a;1. 线程安全的本质线程安全问题的核心是&#xff1a;当多个线程同时访问共享资源&…

使用脚本进行监测以nginx状态页为例

1、首先打开nginx的状态页 location /nginx_status {stub_status on;}2、进行访问测试 curl http://127.0.0.1/nginx_statusActive connections: 1 server accepts handled requests1 1 1 Reading: 0 Writing: 1 Waiting: 03、在客户端编写脚本进行数据采集 编写脚本方便采集不…