🗑️ JVM垃圾回收机制深度解析


文章目录

  • 🗑️ JVM垃圾回收机制深度解析
    • 🔍 垃圾判定算法
      • 🔢 引用计数法
      • 🌐 可达性分析算法
    • 🔄 垃圾回收算法
      • 🏷️ 标记-清除算法
      • 📋 复制算法
      • 🔧 标记-整理算法
      • 🏗️ 分代收集算法
    • 🛠️ 常见垃圾收集器
      • 🔄 Serial 收集器
      • ⚡ ParNew 收集器
      • 🚀 Parallel 收集器
      • 🔧 CMS 收集器
      • 🌟 G1 收集器
    • ⚡ 垃圾回收调优
      • 🔧 常用 JVM 调优参数
      • 🛠️ 调优工具使用:JConsole、VisualVM
        • JConsole
        • VisualVM
    • 🔬 实战案例分析
      • 案例一:内存泄漏排查
      • 案例二:GC停顿时间过长
      • 案例三:频繁的Young GC
    • 📊 总结与展望
      • 垃圾回收技术的发展趋势
      • 最佳实践总结


🔍 垃圾判定算法

在JVM中,垃圾回收的第一步是确定哪些对象是"垃圾"。JVM主要采用两种算法来判断对象是否可以被回收。

🔢 引用计数法

引用计数法是一种直观的垃圾判定方法,其核心思想是:为每个对象添加一个引用计数器,当有引用指向该对象时计数器加1,引用失效时计数器减1,计数器为0时即可回收

public class ReferenceCountingExample {public Object instance = null;public static void main(String[] args) {ReferenceCountingExample objA = new ReferenceCountingExample();ReferenceCountingExample objB = new ReferenceCountingExample();// 对象之间相互引用objA.instance = objB;objB.instance = objA;// 将objA和objB置为null,断开外部引用objA = null;objB = null;// 此时objA和objB指向的对象虽然已经不可能再被访问,// 但由于它们相互引用,引用计数都不为0,导致无法被回收System.gc(); // 触发垃圾回收}
}

引用计数法的优缺点:

优点缺点
实现简单,判定效率高无法解决循环引用问题
对象可以很快被回收计数器增减操作带来额外开销
内存管理的实时性较高需要额外的空间存储计数器

💡 注意:虽然引用计数法简单直观,但由于无法解决循环引用问题,现代JVM(如HotSpot)并不使用此算法作为主要的垃圾判定方法。

🌐 可达性分析算法

可达性分析算法是现代JVM采用的主要垃圾判定算法,其核心思想是:通过一系列称为"GC Roots"的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为"引用链"。如果某个对象到GC Roots之间没有任何引用链相连,则证明此对象是不可能再被使用的,可以被回收

GC Roots
对象B
对象C
对象D
对象E
对象F
对象G
对象H

在Java中,可作为GC Roots的对象包括:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中JNI(Native方法)引用的对象
  5. 活跃线程
public class GCRootsExample {// 静态属性引用的对象作为GC Rootsprivate static Object staticObject;// 常量引用的对象作为GC Rootsprivate static final Object CONST_OBJECT = new Object();public void method() {// 虚拟机栈中引用的对象作为GC RootsObject localObject = new Object();// 使用本地方法nativeMethod();}private native void nativeMethod(); // 本地方法栈中引用的对象作为GC Rootspublic static void main(String[] args) {// main方法是一个活跃线程,其中引用的对象作为GC RootsObject mainObject = new Object();// 创建一个不可达对象Object unreachableObject = new Object();unreachableObject = null; // 断开引用,此对象变为不可达System.gc(); // 触发垃圾回收}
}

可达性分析算法的优缺点:

优点缺点
能解决循环引用问题需要STW(Stop-The-World),暂停所有用户线程
判定更加精确实现复杂
被大多数现代JVM采用可能造成较长时间的停顿

💡 注意:可达性分析算法执行时必须保证整个分析过程的一致性,因此需要STW。JVM后续的优化方向之一就是如何减少STW的时间。

🔄 垃圾回收算法

确定了哪些对象需要回收后,接下来就是如何高效地回收这些对象。JVM主要采用以下几种垃圾回收算法。

🏷️ 标记-清除算法

标记-清除(Mark-Sweep)算法是最基础的垃圾回收算法,分为两个阶段:

  1. 标记阶段:标记出所有需要回收的对象
  2. 清除阶段:统一回收所有被标记的对象
graph LRsubgraph 标记前A1[对象A] --- B1[对象B] --- C1[对象C] --- D1[对象D] --- E1[对象E]endsubgraph 标记后A2[对象A] --- B2[对象B 标记] --- C2[对象C] --- D2[对象D 标记] --- E2[对象E 标记]endsubgraph 清除后A3[对象A] --- C3[对象C] --- 空1((空闲)) --- 空2((空闲)) --- 空3((空闲))end标记前 --> 标记后 --> 清除后classDef normal fill:#d4f9d4,stroke:#333,stroke-width:1px;classDef marked fill:#f9d4d4,stroke:#333,stroke-width:1px;classDef empty fill:#d4d4f9,stroke:#333,stroke-width:1px;class A1,B1,C1,D1,E1,A2,C2,A3,C3 normal;class B2,D2,E2 marked;class 空1,空2,空3 empty;

标记-清除算法的优缺点:

优点缺点
实现简单效率不高,标记和清除两个过程效率都不高
是其他算法的基础产生大量内存碎片,导致无法分配大对象

📋 复制算法

复制(Copying)算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完了,就将还存活的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。

graph LRsubgraph 复制前subgraph From空间A1[对象A] --- B1[对象B] --- C1[对象C] --- D1[对象D] --- E1[对象E]endsubgraph To空间空1((空闲)) --- 空2((空闲)) --- 空3((空闲)) --- 空4((空闲)) --- 空5((空闲))endendsubgraph 复制后subgraph To空间变为From空间A2[对象A] --- C2[对象C] --- 空6((空闲)) --- 空7((空闲)) --- 空8((空闲))endsubgraph From空间变为To空间空9((空闲)) --- 空10((空闲)) --- 空11((空闲)) --- 空12((空闲)) --- 空13((空闲))endend复制前 --> 复制后                                                                                                                                                                                                                                                                                                                                                  classDef normal fill:#d4f9d4,stroke:#333,stroke-width:1px;classDef empty fill:#d4d4f9,stroke:#333,stroke-width:1px;class A1,B1,C1,D1,E1,A2,C2 normal;class 空1,空2,空3,空4,空5,空6,空7,空8,空9,空10,空11,空12,空13 empty;

复制算法的优缺点:

优点缺点
实现简单,运行高效内存利用率低,只有一半内存可用
没有内存碎片对象存活率高时,复制操作开销大
适合新生代回收需要额外空间做分配担保

💡 实际应用:HotSpot VM的新生代使用了复制算法的变种 - Eden和Survivor区的比例是8:1:1,每次只有10%的内存会被"浪费"。

🔧 标记-整理算法

标记-整理(Mark-Compact)算法是针对老年代对象存活率高的特点设计的。标记过程与标记-清除算法一样,但后续步骤不是直接清理,而是让所有存活的对象都向内存空间一端移动,然后清理掉边界以外的内存。

graph LRsubgraph 标记前A1[对象A] --- B1[对象B] --- C1[对象C] --- D1[对象D] --- E1[对象E]endsubgraph 标记后A2[对象A] --- B2[对象B 标记] --- C2[对象C] --- D2[对象D 标记] --- E2[对象E 标记]endsubgraph 整理后A3[对象A] --- C3[对象C] --- 空1((空闲)) --- 空2((空闲)) --- 空3((空闲))end标记前 --> 标记后 --> 整理后classDef normal fill:#d4f9d4,stroke:#333,stroke-width:1px;classDef marked fill:#f9d4d4,stroke:#333,stroke-width:1px;classDef empty fill:#d4d4f9,stroke:#333,stroke-width:1px;class A1,B1,C1,D1,E1,A2,C2,A3,C3 normal;class B2,D2,E2 marked;class 空1,空2,空3 empty;

标记-整理算法的优缺点:

优点缺点
不会产生内存碎片移动对象需要更新引用,效率较低
内存利用率高需要STW,停顿时间可能较长
适合老年代回收实现复杂

🏗️ 分代收集算法

分代收集算法并不是一种具体的垃圾回收算法,而是根据对象的生命周期特征,将内存划分为几个区域,并在不同区域采用不同的收集算法。

graph TDsubgraph JVM堆内存subgraph 新生代E[Eden区] --- S1[Survivor 1区]E --- S2[Survivor 2区]endsubgraph 老年代O[Old区]endend新生代 -.-> |对象晋升| 老年代classDef eden fill:#f9d4d4,stroke:#333,stroke-width:1px;classDef survivor fill:#d4f9d4,stroke:#333,stroke-width:1px;classDef old fill:#d4d4f9,stroke:#333,stroke-width:1px;class E eden;class S1,S2 survivor;class O old;

分代收集的策略:

  1. 新生代:大多数对象朝生夕灭,存活率低,采用复制算法
  2. 老年代:对象存活率高,采用标记-清除或标记-整理算法

分代收集的对象晋升过程:

public class GenerationalGCExample {public static void main(String[] args) {// 1. 新对象优先在Eden区分配byte[] allocation1 = new byte[30900*1024];// 2. Eden区满,触发Minor GC,存活对象复制到Survivor区byte[] allocation2 = new byte[900*1024];// 3. 多次GC后,对象年龄达到阈值,晋升到老年代for (int i = 0; i < 15; i++) {byte[] allocation3 = new byte[1000*1024];allocation3 = null; // 使对象变为垃圾System.gc(); // 建议JVM进行垃圾回收}// 4. 大对象直接进入老年代byte[] allocation4 = new byte[10*1024*1024];}
}

分代收集算法的优缺点:

优点缺点
针对不同代的特点采用最合适的算法实现复杂
提高了垃圾回收效率需要维护多个内存区域
减少了内存碎片和停顿时间各代之间的对象引用需要特殊处理

🛠️ 常见垃圾收集器

JVM提供了多种垃圾收集器,每种收集器都有其特点和适用场景。

🔄 Serial 收集器

Serial收集器是最基本、历史最悠久的垃圾收集器,它是一个单线程收集器,在进行垃圾收集时,必须暂停所有用户线程。

用户线程Serial收集器线程运行等待触发GCSTW开始暂停垃圾收集完成GCSTW结束恢复运行等待用户线程Serial收集器线程

Serial收集器的特点:

  • 单线程收集
  • 简单高效,对于单CPU环境来说是首选
  • 收集过程中需要STW,停顿时间较长
  • 新生代采用复制算法,老年代采用标记-整理算法
  • 适用于客户端应用,如桌面应用程序

启用参数: -XX:+UseSerialGC

⚡ ParNew 收集器

ParNew收集器是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为和Serial收集器完全一样。

用户线程ParNew收集器线程1ParNew收集器线程2ParNew收集器线程n运行等待等待等待触发GCSTW开始暂停垃圾收集垃圾收集垃圾收集完成GCSTW结束恢复运行等待等待等待用户线程ParNew收集器线程1ParNew收集器线程2ParNew收集器线程n

ParNew收集器的特点:

  • 多线程收集,充分利用多核CPU优势
  • 收集过程中需要STW,但停顿时间比Serial短
  • 新生代采用复制算法
  • 是许多服务端应用首选的新生代收集器
  • 可与CMS收集器配合使用

启用参数: -XX:+UseParNewGC

🚀 Parallel 收集器

Parallel收集器(也称为Parallel Scavenge收集器)是一个新生代收集器,使用复制算法,也是并行的多线程收集器。

public class ParallelGCExample {public static void main(String[] args) {// 设置Parallel收集器参数// -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:GCTimeRatio=19// 创建大量对象触发GCList<byte[]> list = new ArrayList<>();for (int i = 0; i < 1000; i++) {byte[] bytes = new byte[1024 * 1024]; // 1MBlist.add(bytes);if (i % 10 == 0) {list.clear(); // 释放引用,触发GC}}}
}

Parallel收集器的特点:

  • 多线程收集,注重吞吐量
  • 可设置最大垃圾收集停顿时间和吞吐量
  • 自适应调节策略,动态调整参数
  • 新生代采用复制算法,老年代采用标记-整理算法
  • 适用于后台运算而不需要太多交互的应用

启用参数:

  • -XX:+UseParallelGC:新生代使用Parallel Scavenge,老年代使用Serial Old
  • -XX:+UseParallelOldGC:新生代使用Parallel Scavenge,老年代使用Parallel Old

🔧 CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它非常适合互联网站或者B/S系统的服务端,这类应用通常重视服务的响应速度,希望系统停顿时间最短。

用户线程CMS收集器线程运行等待触发GC初始标记(STW)短暂暂停标记GC Roots并发标记继续运行并发标记存活对象重新标记(STW)短暂暂停修正标记结果并发清除继续运行并发清除垃圾对象完成GC运行等待用户线程CMS收集器线程

CMS收集器的工作流程:

  1. 初始标记:标记GC Roots能直接关联到的对象,速度很快,需要STW
  2. 并发标记:进行GC Roots Tracing,与用户线程并发执行
  3. 重新标记:修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,需要STW
  4. 并发清除:清除标记为垃圾的对象,与用户线程并发执行

CMS收集器的特点:

  • 并发收集,低停顿
  • 采用标记-清除算法,会产生内存碎片
  • 对CPU资源敏感
  • 无法处理浮动垃圾(并发清除阶段产生的垃圾)
  • 需要预留一部分内存作为并发收集时的预留空间

启用参数: -XX:+UseConcMarkSweepGC

🌟 G1 收集器

G1(Garbage-First)收集器是一款面向服务端应用的垃圾收集器,它是JDK 9的默认垃圾收集器。G1收集器的设计目标是取代CMS收集器,它同样具有并发和并行、低停顿的特点,同时兼顾了高吞吐量。

G1堆内存
Region 2: Eden
Region 1: Eden
Region 3: Survivor
Region 4: Old
Region 5: Old
Region 6: Humongous
Region 7: Eden
Region 8: Old

G1收集器的工作原理:

  1. 将整个Java堆划分为多个大小相等的独立区域(Region)
  2. 保留分代概念,但不再是物理隔离
  3. 建立可预测的停顿时间模型
  4. 采用"标记-整理"算法,降低内存碎片
  5. 采用"复制"算法,提高回收效率
public class G1GCExample {public static void main(String[] args) {// 设置G1收集器参数// -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=2m// 创建大量对象触发GCList<byte[]> list = new ArrayList<>();Random random = new Random();while (true) {int size = random.nextInt(1024 * 1024); // 0-1MBbyte[] bytes = new byte[size];list.add(bytes);if (list.size() > 10000) {list.subList(0, 5000).clear(); // 释放一部分引用System.gc(); // 建议JVM进行垃圾回收}try {Thread.sleep(1); // 控制速度} catch (InterruptedException e) {e.printStackTrace();}}}
}

G1收集器的特点:

  • 并发与并行收集
  • 分代收集
  • 空间整合(标记-整理+复制算法,无内存碎片)
  • 可预测的停顿时间模型
  • 适用于大内存、多核CPU的服务器环境

启用参数: -XX:+UseG1GC

⚡ 垃圾回收调优

垃圾回收调优是JVM性能优化的重要部分,合理的GC参数配置可以显著提升应用性能。

🔧 常用 JVM 调优参数

参数说明示例值
-Xms初始堆大小-Xms4g
-Xmx最大堆大小-Xmx4g
-Xmn新生代大小-Xmn1g
-XX:SurvivorRatioEden区与Survivor区的比例-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold对象晋升老年代的年龄阈值-XX:MaxTenuringThreshold=15
-XX:ParallelGCThreads并行GC线程数-XX:ParallelGCThreads=4
-XX:ConcGCThreads并发GC线程数-XX:ConcGCThreads=2
-XX:InitiatingHeapOccupancyPercent触发并发GC的堆占用率阈值-XX:InitiatingHeapOccupancyPercent=45
-XX:MaxGCPauseMillis最大GC停顿时间-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSizeG1收集器的Region大小-XX:G1HeapRegionSize=4m

不同场景的JVM参数配置示例:

  1. 高吞吐量场景(后台批处理):
java -Xms4g -Xmx4g -Xmn1g -XX:+UseParallelGC -XX:ParallelGCThreads=8 -XX:+UseNUMA -jar app.jar
  1. 低延迟场景(交互式应用):
java -Xms4g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:InitiatingHeapOccupancyPercent=45 -jar app.jar
  1. 内存受限场景(嵌入式或容器环境):
java -Xms256m -Xmx512m -XX:+UseSerialGC -jar app.jar

🛠️ 调优工具使用:JConsole、VisualVM

JConsole

JConsole是JDK自带的图形化监控工具,可以监控本地和远程的Java应用程序。

使用步骤:

  1. 启动JConsole:在命令行中输入jconsole
  2. 选择要监控的Java进程
  3. 查看内存、线程、类、VM摘要等信息

JConsole监控内存的关键指标:

  • 堆内存使用情况
  • 非堆内存使用情况
  • 各代内存使用情况
  • GC次数和时间
VisualVM

VisualVM是一个功能更强大的监控和分析工具,它集成了多种JDK命令行工具的功能。

使用步骤:

  1. 启动VisualVM:在命令行中输入jvisualvm
  2. 选择要监控的Java进程
  3. 查看概述、监视、线程、抽样器、分析器等信息

VisualVM的主要功能:

  • 监控CPU、堆内存、类、线程
  • 执行堆转储和线程转储
  • 分析性能热点
  • 安装插件扩展功能
public class GCTuningExample {public static void main(String[] args) {// 启动参数: -Xms512m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStampsSystem.out.println("GC调优示例启动,进程ID: " + ManagementFactory.getRuntimeMXBean().getName());System.out.println("使用JConsole或VisualVM连接此进程进行监控");List<byte[]> list = new ArrayList<>();int count = 0;try {while (true) {// 创建1MB的对象byte[] bytes = new byte[1024 * 1024];list.add(bytes);count++;if (count % 10 == 0) {// 每创建10个对象,清理一半对象int half = list.size() / 2;list.subList(0, half).clear();System.out.println("已创建对象数: " + count + ", 当前列表大小: " + list.size());}Thread.sleep(100); // 控制速度}} catch (InterruptedException e) {e.printStackTrace();}}
}

🔬 实战案例分析

案例一:内存泄漏排查

问题描述:一个Web应用在运行一段时间后,内存占用持续增加,最终导致OutOfMemoryError。

排查步骤

  1. 收集证据:使用-XX:+HeapDumpOnOutOfMemoryError参数获取堆转储文件
  2. 分析堆转储:使用MAT(Memory Analyzer Tool)分析堆转储文件
  3. 定位问题:发现大量的Session对象未被释放
  4. 解决方案:修复Session管理代码,确保不再使用的Session被及时释放
public class MemoryLeakExample {// 问题代码:使用静态集合存储对象,导致内存泄漏private static final Map<String, Object> cache = new HashMap<>();public void addToCache(String key, Object value) {cache.put(key, value); // 对象被添加后永远不会被移除}// 修复后的代码:使用WeakHashMap或添加过期机制private static final Map<String, Object> fixedCache = new WeakHashMap<>();public void addToFixedCache(String key, Object value) {fixedCache.put(key, value); // 当key不再被引用时,对应的entry会被自动移除}
}

案例二:GC停顿时间过长

问题描述:一个交易系统在高峰期出现间歇性响应缓慢,监控发现是GC停顿时间过长导致的。

排查步骤

  1. 收集GC日志:使用-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log参数
  2. 分析GC日志:使用GCViewer等工具分析GC日志
  3. 定位问题:发现Full GC频繁发生,停顿时间长
  4. 解决方案:从Serial Old收集器切换到G1收集器,并调整参数

优化前的JVM参数

-Xms2g -Xmx2g -XX:+UseParallelGC

优化后的JVM参数

-Xms4g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:InitiatingHeapOccupancyPercent=45

案例三:频繁的Young GC

问题描述:一个数据处理应用频繁出现Young GC,影响整体吞吐量。

排查步骤

  1. 监控GC活动:使用VisualVM观察GC频率和内存使用情况
  2. 分析对象分配:使用-XX:+PrintTenuringDistribution参数查看对象年龄分布
  3. 定位问题:发现大量短生命周期的小对象被频繁创建
  4. 解决方案:优化代码,减少对象创建,增加对象复用
public class FrequentYoungGCExample {// 问题代码:在循环中频繁创建对象public void processData(List<String> data) {for (String item : data) {// 每次迭代都创建新的StringBuilderStringBuilder builder = new StringBuilder();builder.append("Processing: ").append(item);System.out.println(builder.toString());}}// 优化后的代码:复用StringBuilder对象public void processDataOptimized(List<String> data) {StringBuilder builder = new StringBuilder();for (String item : data) {builder.setLength(0); // 清空StringBuilderbuilder.append("Processing: ").append(item);System.out.println(builder.toString());}}
}

📊 总结与展望

垃圾回收技术的发展趋势

  1. 低延迟垃圾收集器:如ZGC(Z Garbage Collector)和Shenandoah,它们的目标是将GC停顿时间控制在10ms以内,无论堆的大小如何

  2. 并发收集的改进:减少或消除STW时间,提高并发收集效率

  3. 自适应调优:更智能的GC参数自动调整,减少人工干预

  4. 大内存优化:针对TB级别内存的优化,支持超大堆

  5. 非易失性内存支持:利用新型存储技术,如Intel的Optane持久内存

最佳实践总结

  1. 选择合适的垃圾收集器

    • 吞吐量优先:Parallel收集器
    • 响应时间优先:CMS或G1收集器
    • 大内存低延迟:ZGC或Shenandoah收集器
  2. 合理设置内存大小

    • 避免设置过大的堆内存,增加GC压力
    • 避免设置过小的堆内存,导致频繁GC
    • 新生代与老年代的比例通常为1:2或1:3
  3. 优化对象生命周期

    • 减少临时对象的创建
    • 使用对象池复用对象
    • 注意集合类的使用,避免内存泄漏
  4. 监控与分析

    • 定期检查GC日志和内存使用情况
    • 使用专业工具分析性能瓶颈
    • 建立性能基准,及时发现异常

如果这篇博客对你有帮助,不要忘记点赞、收藏和分享哦!

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

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

相关文章

Docker:容器化技术的基石与实践指南

在现代软件开发和部署中&#xff0c;Docker 作为一种领先的容器化平台&#xff0c;已经成为了开发人员和运维工程师不可或缺的工具。它不仅简化了应用的部署过程&#xff0c;还提高了应用的可移植性和可扩展性。本文将深入探讨 Docker 的核心概念、基本操作以及如何在实际项目中…

java web7(黑马)

Filter简介概念: Filter 表示过滤器&#xff0c;是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。过滤器一般完成一些通用的操作&#xff0c;比如:权限控制、统一编码处理、敏感字符处理等等.快速入…

React-forwardRef-useImperativeHandle

forwardRef 暴露dom节点作用&#xff1a;使用ref暴露DOM节点给父组件案例例如在父组件中想要获取子组件input的输入值&#xff0c;和让input获取焦点父组件import { Button } from antd-mobile import Son from "./components/son"; import { useState,useRef } fro…

Unity 用AI自动开发游戏----Cursor研究(实现一套利用Cursor生成模板快速实现原型的框架)

Unity 快速原型开发框架&#xff08;基于 Cursor AI&#xff09; &#x1f9e9; 框架简介 本框架结合了 AI 编程助手 Cursor 的代码生成能力&#xff0c;构建出一套适用于 Unity 项目的模块化原型开发架构。它旨在极大提升开发效率、降低试错成本&#xff0c;特别适用于快速搭…

D触发器实现2分频verilog及电路

使用D触发器完成2分频电路即通过时钟的上升沿或下降沿到来时进行翻转得到&#xff0c;信号的两个状态所占时间长度相同&#xff0c;因此它的输出时钟的占空比为50%。 D触发器实现2分频的电路图如下所示&#xff1a;通过将D触发器2分频电路级联&#xff0c;可实现输入时钟的2N倍…

UniApp完美对接RuoYi框架开发企业级应用

UniApp完美对接RuoYi框架的完整方案及可开发系统类型&#xff0c;结合企业级实践与开源项目经验整理而成&#xff0c;涵盖技术对接、系统设计及实战案例。 &#x1f527; 一、UniApp与RuoYi对接全流程 1. 后端配置&#xff08;RuoYi-Vue/RuoYi-Cloud&#xff09; 跨域支持 在网…

【通识】深度学习理论基础

1. 深度学习导论 导论和简介的基础知识和路径。 深度学习的各项涵盖范围&#xff1a;深度学习MLPs&#xff0c;然后是机器学习、逻辑回归&#xff0c;知识基础等等 1&#xff09;连结神经网络等等&#xff1a;Cybernetics控制论&#xff0c;Connectionism连结主义&#xff0…

sql-labs(11-12)-万能密码登录

sql-labs(11-12)万能密码登录 第十一关&#xff1a; 这关是一个登陆口&#xff0c;也是一个sql注入的漏洞&#xff0c;也就是常说的万能密码。 在输入框账号密码种分别输入 1’ 和1’ 页面会报错。后台使用的单引符号进行的拼接。账号输入1’ or ‘1’‘1 密码输入 1’ or …

MsSql 其他(2)

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨Mysql中的MVCC 一、MVCC 的核心目标与设计背景 MVCC&#xff08;Multi-Version Concurrency Control&#xff0c;多版本并发控制&#xff09; 是 InnoDB 存储引擎为实现高并发事务处理而设计的核心机制。其核心目标是&#xff1a;在不牺牲事务隔…

解决本地部署n8n,域名访问为什么一直有connection lost的报错

问题&#xff1a;本地部署的n8n服务用IP访问一切都正常&#xff0c;但是使用域名后报错connection lost思路&#xff1a;首先怀疑是ngnix配置问题或者是docker中的环境问题查看docker logsOrigin header does NOT match the expected origin. (Origin: "nxxx.online:1181&…

传统架构开发VS PREEvision:一场效率与可靠性的降维打击

当前&#xff0c;整车功能数量激增&#xff0c;意味着需要更庞大的整车数据库、更复杂的硬件传感器与执行器网络、更密集的跨系统交互接口以及更难以预测的耦合效应。这样一来&#xff0c;单一功能的微小改动&#xff0c;可能会因复杂的依赖关系而引发意想不到的连锁反应&#…

深度学习基础1

一、张量 张量其实就是数组&#xff0c;不过是在深度学习中是这样的叫法 1.张量的创建 &#xff08;1&#xff09;基本创建方式 torch.tensor()&#xff1a;根据指定数据创建张量 import torch import numpy as np """创建张量标量""" data to…

力扣网编程274题:H指数之普通解法(中等)

一. 简介 本文记录力扣网上涉及数组&#xff0c;排序方面的编程题&#xff1a;H指数。 二. 力扣网编程274题&#xff1a;H指数&#xff08;中等&#xff09; 给你一个整数数组 citations &#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研…

iptables防火墙,多IP环境下, 指定某个目的IP地址通过某个本地IP访问,策略路由!

需求在CentOS 7.9中&#xff0c;若需从特定源IP&#xff08;10.0.0.3&#xff09;访问目标网段 1.1.1.0/24方法一&#xff1a;策略路由&#xff08;支持网段&#xff09;1. 创建自定义路由表# 添加名为custom_table的路由表&#xff08;ID200&#xff09; echo "200 custo…

数字孪生技术引领UI前端设计新趋势:数据可视化与交互设计的深度融合

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!一、引言&#xff1a;数字孪生驱动 UI 设计的范式革新在大数据与三维可视化技术爆发的今天&…

【机器学习笔记 Ⅱ】6 激活函数

激活函数是神经网络的核心组件&#xff0c;其作用远不止“引入非线性”。以下是系统化的解析&#xff1a;1. 核心作用 (1) 引入非线性没有激活函数&#xff1a;多层神经网络等价于单层线性变换&#xff08;矩阵连乘仍是线性&#xff09;。加入激活函数&#xff1a;每层通过非线…

AI无标记动捕如何结合VR大空间技术打造沉浸式游戏体验

随着数字科技的迅猛发展&#xff0c;VR大空间技术正逐步成为各行业探索沉浸式体验的重要方向。在VR游戏领域&#xff0c;市场对于高度沉浸式体验的需求日益增长&#xff0c;而传统VR游戏主要依赖手柄和基础体感进行交互&#xff0c;而在VR大空间中&#xff0c;用户可以通过全身…

Qt智能指针

在 Qt 框架中&#xff0c;智能指针用于自动管理对象的生命周期&#xff0c;防止内存泄漏。以下是 Qt 中主要的智能指针及其用法详解&#xff1a;1. QScopedPointer作用&#xff1a;独占所有权&#xff0c;超出作用域时自动释放对象&#xff08;类似 std::unique_ptr&#xff09…

408第三季part2 - 计算机网络 - 信道利用率

理解t1是发送帧的传输时间t2是确认帧的传输时间中间是传播过程这整个过程就是发送周期任何题目会有以下几种情况题目这里数据帧和确认帧长度是一样的t1 t2然后把t1的传输数据算出来然后传播是0.2sd停止等待 k1确认帧忽略t2 0t1算好后&#xff0c;求数据帧的长度下面是速率&…

Android framework 开发者模式下,如何修改动画过度模式

Android framework 开发者模式下&#xff0c; 如何修改动画过度模式 开发者模式下&#xff0c;动画过度 模式1.0→0.5&#xff0c;按如下方式修改。 开发云 - 一站式云服务平台 .../core/java/com/android/server/wm/WindowManagerService.java | 8 ---- 1 file changed, …