【JVM】万字总结GC垃圾回收
【JVM】GC垃圾回收
概念
在程序运行过程中,会不断创建对象来使用内存,当这些对象不再被引用时,其所占用的内存若不及时释放,会导致内存占用不断增加,最终可能引发内存溢出。GC 机制能自动检测并回收这些不再使用的对象所占用的内存。
JVM内存管理
在JVM中,运行时数据区由程序计数器、java虚拟机栈、本地方法栈、方法区、堆五部分组成。
其中程序计数器、java虚拟机栈、本地方法栈三部分是线程私有的,伴随着线程的创建而创建,线程的销毁而销毁,方法区的栈帧在执行完方法后就会弹出并释放对应的内存。
方法区的回收
1. 方法区/元空间会回收哪些数据?
方法区主要回收以下两类数据:
- 废弃的类(Unloaded Classes)
- 当一个类不再被任何地方引用(无实例对象、无ClassLoader加载、无反射调用等),JVM可以卸载该类。
- 卸载后,其类信息、方法字节码、运行时常量池等会被回收。
- 废弃的常量(Obsolete Constants)
- 字符串常量池(String Table)中的字符串,如果没有被引用,可以被回收。
- 运行时常量池(Runtime Constant Pool)中的符号引用(Symbolic References)也可以被回收。
2. 方法区的垃圾回收触发条件
(1) 类的卸载条件
一个类可以被卸载,必须满足 所有 以下条件:
- 该类所有的实例都已被回收(堆中无该类的对象)。
- 加载该类的
ClassLoader
已被回收(例如自定义类加载器被卸载)。 - 该类对应的
java.lang.Class
对象没有被任何地方引用(如反射、静态变量等)。
import java.lang.ref.WeakReference;public class ClassUnloadDemo {static WeakReference<Class<?>> weakClassRef;public static void main(String[] args) throws Exception {// 1. 创建自定义类加载器CustomClassLoader loader = new CustomClassLoader();// 2. 加载类Class<?> clazz = loader.loadClass("TempClass");weakClassRef = new WeakReference<>(clazz);// 3. 创建实例Object instance = clazz.getDeclaredConstructor().newInstance();// 4. 检查初始状态System.out.println("初始状态: " + (weakClassRef.get() != null ? "类存在" : "类已回收"));// 5. 满足类卸载条件(分步骤演示)System.out.println("\n=== 步骤1: 回收所有实例 ===");instance = null;System.gc();Thread.sleep(100);System.out.println("实例回收后: " + (weakClassRef.get() != null ? "类存在" : "类已回收"));System.out.println("\n=== 步骤2: 回收ClassLoader ===");loader = null;System.gc();Thread.sleep(100);System.out.println("ClassLoader回收后: " + (weakClassRef.get() != null ? "类存在" : "类已回收"));System.out.println("\n=== 步骤3: 释放Class引用 ===");clazz = null;System.gc();Thread.sleep(100);System.out.println("Class引用释放后: " + (weakClassRef.get() != null ? "类存在" : "类已回收"));}
}class CustomClassLoader extends ClassLoader {@Overrideprotected Class<?> findClass(String name) {// 简单模拟类字节码byte[] b = new byte[100];return defineClass(name, b, 0, b.length);}
}
初始状态: 类存在=== 步骤1: 回收所有实例 ===
实例回收后: 类存在=== 步骤2: 回收ClassLoader ===
ClassLoader回收后: 类存在=== 步骤3: 释放Class引用 ===
Class引用释放后: 类已回收
(2) 常量的回收条件
- 字符串常量:如果字符串常量池中的某个字符串不再被任何变量引用,可以被回收(类似弱引用)。
import java.lang.ref.WeakReference;public class StringConstantGCDemo {public static void main(String[] args) {// 1. 创建字符串常量String str = "HelloJVM"; // 进入常量池WeakReference<String> weakRef = new WeakReference<>(str);// 2. 初始状态System.out.println("初始状态: " + weakRef.get());// 3. 释放引用System.out.println("\n释放引用后...");str = null;// 4. 尝试触发GCfor (int i = 0; i < 3; i++) {System.gc();try {Thread.sleep(100);} catch (InterruptedException e) {}System.out.println("GC后状态: " + (weakRef.get() != null ? "字符串存在" : "字符串已回收"));}}
}
输出示例:
初始状态: HelloJVM释放引用后...
GC后状态: 字符串存在
GC后状态: 字符串存在
GC后状态: 字符串已回收
说明:
字符串常量在没有强引用时可能被回收
回收时机取决于JVM实现(可能需要多次GC)
- 运行时常量池:如果某个符号引用(如类名、方法名)不再被使用,可以被回收。
import java.lang.reflect.Method;
import java.lang.ref.WeakReference;public class SymbolUnloadDemo {static WeakReference<Class<?>> weakClassRef;public static void main(String[] args) throws Exception {// 1. 加载类(创建符号引用)Class<?> clazz = Class.forName("java.util.ArrayList");weakClassRef = new WeakReference<>(clazz);// 2. 使用方法(创建方法符号引用)Method method = clazz.getMethod("size");// 3. 初始状态System.out.println("初始状态: " + weakClassRef.get().getSimpleName());// 4. 释放引用clazz = null;method = null;// 5. 触发GCSystem.gc();Thread.sleep(100);// 6. 检查状态System.out.println("GC后状态: " + (weakClassRef.get() != null ? "符号引用存在" : "符号引用已回收"));}
}
输出示例:
初始状态: ArrayListGC后状态: 符号引用已回收
说明:
- 当类/方法不再被使用时,其符号引用可能被回收
- 符号引用回收通常发生在类卸载过程中
堆的回收
一、引用计数法和可达性分析算法
1.引用计数法
引用计数法会为每个对象维护一个引用计数器,当对象被引用时加1,取消引用时减1。
缺点:
循环引用问题:
class Node {Node next;
}public static void main(String[] args) {Node a = new Node(); // 计数=1Node b = new Node(); // 计数=1a.next = b; // b计数=2b.next = a; // a计数=2a = null; // a计数=1b = null; // b计数=1// 循环引用导致内存泄漏!
}
2.可达性分析算法
可达性分析算法将对象分为了两种:GC Root对象、普通对象。
可达性分析算法以GC Root的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Root之间没有任何引用链项链时,则证明此对象是不可用的,可判定为可回收对象。不会产生循环引用问题。
核心 GC Root 类型
- 线程Thread对象。
- 系统类加载器加载的java.lang.Class对象。
- 监视器对象,用来保存同步锁synchronized关键字持有的对象。
- 本地方法调用时使用的全局对象
二、五种对象引用
在java中,对象引用主要分为五种类型:强引用、软引用、
弱引用、虚引用、终结期引用。
1. 强引用
最常见的引用类型,默认创建的引用都是强引用。
Object obj = new Object(); // 强引用
特点:
- 只要强引用存在,对象永远不会被回收
- 内存不足时抛出
OutOfMemoryError
- 使用场景:普通对象创建
回收条件:
obj = null; // 显式解除引用
2.软引用
// 创建软引用
SoftReference<byte[]> softRef = new SoftReference<>(new byte[10*1024*1024]); // 10MB
特点:
- 内存充足时不会被回收
- 内存不足时可能被回收
- 使用场景:图片缓存、网页缓存
3.弱引用
// 创建弱引用
WeakReference<Object> weakRef = new WeakReference<>(new Object());// 获取对象
Object obj = weakRef.get();
if (obj != null) {System.out.println("弱引用对象存在");
}// 强制垃圾回收
System.gc();// 再次检查
if (weakRef.get() == null) {System.out.println("对象已被回收");
}
特点:
- 下一次垃圾回收时立即回收(无其他引用)
- 不会阻止垃圾回收
- 使用场景:临时对象映射、监控对象状态
虚引用和终结器引用在常规开发中是不会使用的。
4.虚引用
虚引用也被称为幽灵引用或者幻影引用,它不会影响对象的生命周期,也无法通过虚引用获取到对象实例。虚引用的主要作用是在对象被垃圾回收时收到一个系统通知。创建虚引用时必须关联一个引用队列(ReferenceQueue
),当虚引用引用的对象被垃圾回收后,该虚引用会被加入到关联的引用队列中。
使用场景
虚引用主要用于在对象被回收时执行一些额外的清理操作,比如管理直接内存。直接内存不受 Java 堆内存管理,需要手动释放,使用虚引用可以在对象被回收时释放对应的直接内存资源。
示例代码
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;public class PhantomReferenceExample {public static void main(String[] args) {// 创建一个对象Object obj = new Object();// 创建引用队列ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();// 创建虚引用,关联对象和引用队列PhantomReference<Object> phantomReference = new PhantomReference<>(obj, referenceQueue);// 断开强引用obj = null;// 建议 JVM 进行垃圾回收System.gc();try {// 等待一段时间,让垃圾回收有机会执行Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 检查引用队列中是否有虚引用if (referenceQueue.poll() != null) {System.out.println("对象已被垃圾回收,虚引用已加入引用队列");} else {System.out.println("对象尚未被垃圾回收");}}
}
特点:
- 无法通过
get()
获取对象 - 对象被回收时收到通知
- 使用场景:直接内存清理、资源释放
5.终结器引用
class ResourceHolder {private byte[] data = new byte[1024*1024]; // 1MB@Overrideprotected void finalize() throws Throwable {try {System.out.println("执行 finalize() 清理资源");} finally {super.finalize();}}
}// 使用
new ResourceHolder();
System.gc();
特点:
- 对象被回收前会调用
finalize()
方法 - 执行时间不确定
- 使用场景:传统资源清理(已被
try-with-resources
取代)
try-with-resources
try (InputStream in = new FileInputStream("input.txt");OutputStream out = new FileOutputStream("output.txt")
) {byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = in.read(buffer)) != -1) {out.write(buffer, 0, bytesRead);}
} // 自动关闭 in 和 out
三、垃圾回收算法
1.标记清除算法
工作原理
标记清除算法分为两个阶段:标记阶段和清除阶段。
标记阶段:从 GC Roots 开始遍历所有可达对象,将这些存活的对象标记出来。
清除阶段:遍历整个堆内存,将未被标记的对象(即垃圾对象)所占用的内存空间释放。
- 优点:实现简单,不需要额外的内存空间。
- 缺点:会产生大量内存碎片。随着时间推移,内存碎片可能导致无法分配足够大的连续内存空间给新对象,即使堆中总的可用内存足够。
2.复制算法
工作原理
复制算法(Copying)将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
- 优点:实现简单,运行高效,不会产生内存碎片,因为每次复制后都是连续的内存空间。
- 缺点:会浪费一半的内存空间,因为总有一块内存区域处于闲置状态。
3.标记整理算法
工作原理
标记整理算法结合了标记清除算法和复制算法的优点。同样分为两个阶段:
- 标记阶段:和标记清除算法的标记阶段相同,从 GC Roots 开始遍历,标记出所有存活的对象。
- 整理阶段:将所有存活的对象向内存的一端移动,然后直接清理掉端边界以外的内存。
优缺点
- 优点:避免了内存碎片问题,同时也不需要像复制算法那样浪费一半的内存空间。
- 缺点:需要移动对象,这会带来一定的性能开销,尤其是在对象较多时。
4.分代GC
分代GC的核心思想是根据对象的存活时间将堆内存划分成不同区域(代),不同代采用不同的垃圾回收算法。大部分的对象存活时间都很短,少部分的对象存活时间较长,基于这个特性,将堆内存划分为新生代和老年代。
新生代:新创建的对象会被分配在新生代,新生代又细分为一个Eden区和两个survivor
区(一般称为 Survivor0
和 Survivor1
或 From
和 To
)。
老年代:经历多次垃圾回收仍存活的对象会被移动到老年代。
回收算法与回收频率
新生代
- 回收算法:复制算法。垃圾回收时,会把
Eden
区和Survivor
区中幸存的对象复制到另一个Servivor
区,复制后Eden
区和Survivor
区(From
)被清空。 - 回收频率:新生代垃圾回收(
Minor GC
)的频率较高,因为新对象不断创建,Eden
区内存增速很快。
老年代
- 回收算法:标记清除算法或标记整理算法。
- 回收频率:老年代垃圾回收(
Full GC
)的频率低,因为老年代中的对象相对稳定,当Full GC
的耗时通常比Minor GC
长。
分代GC流程
1.对象创建:新创建的对象首先被分配到新生代的 Eden 区。
2.Minor GC:当 Eden 区空间不足时,触发 Minor GC。将 Eden 区和一个 Survivor 区中存活的对象复制到另一个 Survivor 区,对象每经历一次 Minor GC 且存活,其年龄计数器加 1。
3.对象晋升:当对象年龄达到一定阈值(默认 15),会被晋升到老年代。除了年龄达到阈值外,还有其他情况会导致对象晋升到老年代,如 Survivor 区空间不足时,存活对象会直接晋升到老年代。
4.Full GC:当老年代空间不足时,触发 Full GC。对整个堆进行垃圾回收,包括新生代和老年代。通常会先进行 Minor GC,再对老年代进行垃圾回收。
四、垃圾回收器
在 Java 垃圾回收中,基于分代假说(大部分对象生命周期短,只有少数对象存活时间长),堆内存通常被划分为新生代和老年代。不同的垃圾回收器负责回收不同代的内存,并且经常需要配合使用。
1.Serial
-
新生代垃圾回收器
-
复制算法
-
特点:单线程工作。进行垃圾回收器时,会暂停所有的用户线程。
2.Serial Old
- 老年代垃圾回收器
- 标记整理算法
- 特点:Serial 收集器的老年代版本,同样是单线程工作,进行垃圾回收时,暂停所有的用户线程。
- 作为 CMS 收集器在发生“Concurrent Mode Failure”时的后备预案(当 CMS 无法及时回收老年代空间时,会退回到 Serial Old 进行 Full GC)。
3.ParNew
-
新生代垃圾回收器
-
复制算法
-
特点:Serial的多线程版本。垃圾回收时,多线程执行,但同样会阻塞所有的用户线程。
4.CMS
-
老年代垃圾回收器
-
标记清除法
-
特点:以获取最短的STW时间为目标,尽可能使用户线程和垃圾回收线程并发工作
-
初始标记
标记GC Root直接关联的对象,对象相对较少,速度很快。
-
并发标记
从初始标记的对象出发,标记出所有存活的对象,与用户线程并发执行。
-
重新标记
修正并发标记时因用户线程继续运行而导致的标记的变化。
-
并发清理
与用户线程并发执行,清理未被标记的对象。
-
缺点:
- CMS使用了标记-清除算法,在垃圾收集结束之后会出现大量的内存碎片,CMS会在Full GC时进行碎片的整理。 这样会导致用户线程暂停,可以使用-XX:CMSFullGCsBeforeCompaction=N 参数(默认0)调整N次Full GC之 后再整理。
- 无法处理在并发清理过程中产生的“浮动垃圾”,不能做到完全的垃圾回收。
- 如果老年代内存不足无法分配对象,CMS就会退化成Serial Old单线程回收老年代。
5.Parallel Scavenge
- 新生代垃圾回收器
- 复制算法
- 特点:
- 多线程回收:采用多线程并行的方式进行垃圾回收,在多核 CPU 环境下,能充分利用 CPU 资源,提升垃圾回收效率。
- 关注吞吐量:以达到可控制的吞吐量为目标。吞吐量指的是在应用运行期间,CPU 用于执行用户代码的时间与 CPU 总消耗时间的比值,即 吞吐量 = 用户代码执行时间 / (用户代码执行时间 + 垃圾回收时间)。高吞吐量意味着能高效利用 CPU 资源,尽快完成程序的运算任务。
- 自动调节策略:具备自适应调节策略,通过
-XX:+UseAdaptiveSizePolicy
参数开启(默认开启)。JVM 会根据系统运行情况自动调整新生代大小、Eden
区和Survivor
区的比例、晋升老年代对象的年龄等参数,以达到预设的吞吐量目标。
-XX:ParallelGCThreads
:设置并行垃圾回收的线程数。一般默认值会根据 CPU 核心数动态调整,可根据实际情况手动设置,以达到更好的性能。-XX:MaxGCPauseMillis
:设置垃圾回收的最大停顿时间(毫秒),JVM 会尽力保证垃圾回收停顿时间不超过该值,但可能会影响吞吐量。-XX:GCTimeRatio
:设置吞吐量大小,取值范围是 1 - 99,默认值为 99,表示垃圾回收时间占总时间的比例不超过1 / (1 + 99) = 1%
。
6.Parallel Old
- 老年代回收器
- 标记整理法
- 工作机制:
- 标记阶段:多线程,标记存活对象,STW暂停所有用户线程。
- 整理阶段:多线程,经存活对象移向一端,并清理端边界外的内存,暂停所有用户线程。
- 高吞吐量:多线程并行回收机制,能充分利用多核 CPU 资源,减少垃圾回收的时间开销,提高应用的整体吞吐量。
- 避免内存碎片:标记 - 整理算法避免了内存碎片的产生,为后续大对象分配提供连续的内存空间,降低因内存碎片导致的 Full GC 频率。
由于垃圾回收器分为年轻代和老年代,除了G1之外其他垃圾回收器必须成对组合使用。
垃圾回收器组合
Java 8及以前
(1)Serial + Serial Old
- 年轻代:Serial(串行收集器,单线程STW)
- 老年代:Serial Old(串行标记-整理算法)
- 特点:适用于客户端模式或小内存应用(如嵌入式设备),回收时暂停所有用户线程(STW)。
- 启用参数:
-XX:+UseSerialGC
(2)ParNew + CMS
- 年轻代:ParNew(多线程并行回收,STW)
- 老年代:CMS(Concurrent Mark-Sweep,并发标记-清除)
- 特点:适用于低延迟Web应用(如响应时间敏感的服务),以减少停顿时间为目的,但会产生内存碎片。
- 启用参数:
-XX:+UseConcMarkSweepGC
(3)Parallel Scavenge + Parallel Old
-
年轻代:Parallel Scavenge(并行回收,吞吐量优先)
-
老年代:Parallel Old(并行标记-整理)
-
特点:适用于后台计算型应用(如大数据批处理),以最大化吞吐量为目标,允许较长的STW。
-
启用参数:
-XX:+UseParallelOldGC
Java 9及以后
G1
设计目标
- 高吞吐量与低延迟的平衡:G1 旨在同时满足高吞吐量(Parallel Scavenge+Parallel Old)和低延迟(ParNew+CMS)的需求,它能在保证一定吞吐量的前提下,将垃圾回收的停顿时间控制在可接受的范围内。
- 可预测的停顿时间:允许用户指定在一个长度为 M 毫秒的时间片段内,垃圾回收的停顿时间不得超过 N 毫秒。
- 引入版本: JDK 7 ,JDK 9 成为默认 GC。
核心思想与架构
-
Region分区:G1的堆会被划分成多个大小相等的区域(Region)。不在物理划分新生代与老年代。
-
逻辑分区c.0:每个Region被标记为Eden、Survivor、Old、Humongous(存放大于Region一半大小的超大对象)。分代是逻辑上的概念,每个Region可以扮演不同代的角色。
- Garbage-First (回收价值优先):
- 并发全局标记: 使用 SATB算法进行并发标记,找出堆中哪些 Region 包含最多的可回收空间(垃圾)和存活对象最少的 Region。
- 回收选择: 根据用户设置的停顿时间目标,优先选择回收价值最高(即垃圾最多)的 Region 集合进行回收。这就是“Garbage-First”名称的由来。
- 疏散(Evacuation) / 复制: 将选中的 Region 中的存活对象复制到新的、空的 Region 中(复制过程需要 STW)。原 Region 被清空并加入空闲列表。这个过程同时完成年轻代回收(Minor GC)和部分老年代回收(Mixed GC)。
主要阶段:
一、Young-Only阶段(新对象分配与年轻代回收)
-
对象分配:
- 新对象分配在Eden Region中。
- 。当G1判断年轻代区不足(max默认60%),触发一次 Young GC(Minor GC)。
-
Young GC(STW事件):
-
根扫描:从GC Roots(栈、寄存器、全局变量等)开始扫描,标记直接可达对象。
-
对象复制:
- 标记 Eden Region 和 Survivor Region中存活的对象。
- 根据配置的最大暂停时间,将某些区域的存活对象复制到新的Survivor Region 中(年龄+1),达到晋升阈值(
-XX:MaxTenuringThreshold
)的对象复制到 Old Region(老年代)。 - 清空被复制的Region,加入空闲列表。
-
根据最大暂停时间和记录的平均耗时计算最大回收区域。
比如 -XX:MaxGCPauseMillis=n(默认200),每个Region回收耗时40ms,那么这次回收最多只能回收4个Region
-
🔁 循环触发:只要应用持续分 配对象,Young GC会不断发生。
二、触发并发标记周期
- 当老年代占用率达到阈值(
-XX:InitiatingHeapOccupancyPercent
,默认45%)时,G1 会先执行一次并发标记周期。并发标记周期结束后,若满足一定条件,就会触发 Mixed GC。
三、并发标记周期
此阶段为 Mixed GC 做准备,包含以下子阶段
- 初始标记:
- 依附于一次Young GC进行(复用其根扫描结果)(或看作同时发生)。
- 标记所有直接从GC Roots可达的对象(速度快,停顿短)。
- **根区域扫描:
- 扫描Survivor Region(根区域)中指向老年代对象的引用。
- 与用户线程并发执行。
- 并发标记:
- 与用户线程并发执行。
- 遍历整个堆,标记所有可达对象(使用SATB算法确保标记一致性)。
- 最终标记:
- 修正并发标记阶段因用户线程继续运行而导致标记产生变动的那一部分对象的标记记录。
- 需要STW,采用SATB算法。
- 清除阶段:
- STW部分:
- 统计每个Region中存活对象比例。
- 选择回收价值最高的Region加入 CSet(Collection Set)。
- 回收完全空闲的Region。
- 并发部分:重置内部数据结构,为混合GC做准备。
- STW部分:
✅ 标记完成:此时G1已精确知道每个Region的垃圾比例,为Mixed GC提供依据。
四、混合回收 - Mixed GC
- 目标:回收高垃圾比例的老年代Region + 所有年轻代Region(Eden+Survivor)。
- 执行方式:
- 选择回收集合(Collection Set,简称 CSet)
- G1 会根据用户设置的最大停顿时间(
-XX:MaxGCPauseMillis
),结合各个 Region 的回收收益,挑选出新生代的 Eden Region、Survivor Region 以及部分老年代 Region 组成 CSet。
- G1 会根据用户设置的最大停顿时间(
- 复制对象(Evacuation)
- 作用:将 CSet 中存活的对象复制到新的空闲 Region 中。新生代的对象会复制到 Survivor Region 或老年代 Region,老年代的对象也会复制到其他空闲的老年代 Region 中。
- 特点:需要 STW,采用复制算法,复制完成后,原来的 CSet 中的 Region 会被释放,标记为空闲,可用于后续对象分配,且不会产生内存碎片。
- 更新引用(Reference Update)
- 在对象复制完成后,需要更新堆中其他对象对这些被复制对象的引用,确保引用指向新的对象地址。
- 选择回收集合(Collection Set,简称 CSet)
- 完成空间回收:当老年代空间释放到安全水平后,G1重新回到 Young-Only阶段。
五、Full GC
当G1无法满足回收需求时会触发(STW时间长):
- 并发标记失败:如对象分配过快,标记速度跟不上。
- 晋升失败:Young GC时Survivor/Old区空间不足。
- 大对象分配失败:无连续Humongous Region可用。
- 处理方式:退化到单线程的 Serial Old GC(标记-整理算法),进行全堆回收。
相关文章:

【JVM】万字总结GC垃圾回收
【JVM】GC垃圾回收 概念 在程序运行过程中,会不断创建对象来使用内存,当这些对象不再被引用时,其所占用的内存若不及时释放,会导致内存占用不断增加,最终可能引发内存溢出。GC 机制能自动检测并回收这些不再使用的对…...

内网横向之RDP缓存利用
RDP(远程桌面协议)在连接过程中会缓存凭据,尤其是在启用了 "保存密码" 或 "凭据管理器" 功能时。这个缓存的凭据通常是用于自动填充和简化后续连接的过程。凭据一般包含了用户的用户名和密码信息,或者是经过加…...

【Linux网络】传输层TCP协议
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343 🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12891150.html 目录 TCP 协议 TCP 协议段格式 确认应答(ACK)机制 超时重传机制 连接管理机制 …...

不同视角理解三维旋转
在二维空间中,绕任意点旋转可以分解为: 1)平移旋转点到原点,2)绕原点旋转,3)逆平移旋转点; 可用矩阵表示为 , 其中, 表示绕原点旋转 , 为平移矩…...

Adobe Acrobat——设置PDF打印页面的大小
1. 打开 PDF 文件; 2. 点击菜单栏的 “文件” → “打印”; 3. 在打印对话框中,点击 “属性”; 4. 点击 “布局”→ “高级”; 5. 点击 “纸张规格”,选择 “PostScript 自定义页面大小”,然后…...
Android apk装机编译类型: verify、speed-profile, speed与启动耗时
Android apk装机编译类型: verify、speed-profile, speed与启动耗时 Dex2oat (dalvik excutable file to optimized art file) ,对 dex 文件进行编译优化,Android 虚拟机可识别的是dex文件,应用运行过程如果每次都将dex文件加载内存ÿ…...
纹理压缩格式优化
🎯 Unity 项目纹理压缩格式优化终极指南 ——不同平台、不同手机型号,如何正确选择 🧩 什么是纹理压缩(Texture Compression)? Texture压缩 = 减小显存占用,提升加载速度,减轻GPU负担纹理是游戏中最大资源,占用50%+内存正确压缩:减少GPU Bandwidth,提高渲染性能错…...

使用Virtual Serial Port Driver+com2tcp(tcp2com)进行两台电脑的串口通讯
使用Virtual Serial Port Drivercom2tcp或tcp2com进行两台电脑的串口通讯 问题说明解决方案方案三具体操作流程网上教程软件安装拓扑图准备工作com2tcp和tcp2com操作使用串口助手进行验证 方案三存在的问题数据错误通讯延时 问题说明 最近想进行串口通讯的一个测试,…...
【从0-1的HTML】第3篇:html引入css的3种方式
文章目录 HTML中引入CSS的方式行内样式内部样式外部样式yinru.css文件 完整html文件 引入CSS方式的优先级 HTML中引入CSS的方式 HTML:是使用标签来描述网页元素 CSS:是Cascading Style Sheets,层叠样式表,用来控制样式来显示网页…...

数智破局·生态共生:重构全球制造新引擎 2025 WOD制造业数字化博览会即将在沪盛大启幕
共探数智化未来,共创新质生产力。2025年6月17日—19日,上海浦东新国际博览中心将迎来全球制造业数字化转型的盛会——WOD制造业数字化博览会。作为全球首个聚焦制造业数字化全场景的专业展会,本届展会以“数智破局生态共生:重构全…...
machine_env_loader must have been assigned before creating ssh child instance
在主机上执行roslaunch命令时,报错:machine_env_loader must have been assigned before creating ssh child instance。 解决办法: 打开hostos文件,检查local host 前的内部ip是否正常。操作示例: 先输入下方指令打…...

BGP/MPLS IP VPN跨域解决方案
目录 MPLS VPN跨域方案出现背景: MPLS VPN回顾 VRF(Virtual Route Forward)虚拟路由转发 MPLS(Multiple Protcol Label Swtich)多协议标签交换 MP-BGP多协议BGP MPLS VPN跨域OptionA 控制平面: 转发平面: 总结: 挑战: MPLS VPN跨域OptionB 非RR场景: 控制平面: 转发…...
C语言-10.字符串
10.1字符串 10.1-1字符串 字符数组 char word[] = {‘H’,‘e’,‘l’,‘l’,‘o’,‘!’}; word[0]Hword[1]eword[2]lword[3]lword[4]oword[5]!这不是C语言的字符串,因为不能用字符串的方式做计算 字符串 char word[] = {‘H’,‘e’,‘l’,‘l’,‘o’,‘!’}; word[0]Hwo…...

backend 服务尝试连接 qdrant 容器,但失败了,返回 502 Bad Gateway 问题排查
遇到的问题是: backend 报错:502 Bad Gateway 来自 Qdrant → 导致接口 /api/chat 返回 500 Internal Server Error并且日志中提示: QDRANT_URL http://qdrant:6333✅ 问题分析 这个错误的根本原因是: 你的 backend 服务尝试连…...
硬件学习笔记--66 MCU的DMA简介
DMA(Direct Memory Access,直接存储器访问)是MCU中一种重要的数据传输机制,它允许外设与存储器之间或存储器与存储器之间直接传输数据,而无需CPU的持续干预。 1、DMA的基本原理 1.1 核心概念: 1…...

18. Qt系统相关:多线程
一、概述 在Qt中,使用QThread类对系统线程进行了封装。QThread代表一个在应用程序中可独立控制的线程,也可以和进程中的其他线程共享数据。 二、QThread常用API 三、QThread使用 自定义一个类,继承自QThread,并且只有一个线程处…...

6个月Python学习计划 Day 14 - 异常处理基础( 补充学习)
第二周 Day 8 - Python 函数基础 Day 9 - 函数进阶用法 Day 10 - 模块与标准库入门 Day 11 - 列表推导式、内置函数进阶、模块封装实战 Day 12 - 字符串处理 & 文件路径操作 Day 13 - 文件操作基础 🎯 今日目标 理解异常的概念和常见异常类型掌握 try-except …...

使用jstack排查CPU飙升的问题记录
最近,看到短视频传播了一个使用jstack来协助排查CPU飙升的案例。我也是比较感兴趣,参考了视频博主的流程,自己做了下对应案例的实战演练,在此,想做一下,针对相关问题模拟与排查演练的实战过程记录。 案例中…...

cursor如何开启自动运行模式
在Cursor中,开启自动运行模式即启用“Yolo Mode”,具体操作如下: 按下Ctrl Shift J(Windows/Linux)或Cmd Shift J(Mac)打开Cursor设置。导航到“Features”(功能)选…...

SecureCRT 设置超时自动断开连接时长
我们在使用SecureCRT 连接服务器时,经常性出现2分钟未操作已连接的服务器,就会自动断开连接,此时需要重新连接,非常影响服务器操作,本文可以很好带领大家解决这种问题。...
《复制粘贴的奇迹:原型模式》
📖 背景故事 创业初期,小明每天加班写配送路线、配送策略、营销套餐。可当业务做大后,他发现大家常常下单“上次那个套餐”—— “老板,再来一个上次的奶茶水果!” “老样子,照搬昨天晚上的宵夜套餐&#…...

IEC 61347-1:2015 灯控制装置安全标准详解
IEC 61347-1:2015灯控制装置安全标准详解 IEC 61347-1:2015 是国际电工委员会(IEC)发布的灯控制装置第1部分:通用要求和安全要求的核心标准,为各类照明用电子控制设备设定了全球通用的安全基准。该标准适用于独立式或内置于灯具/…...

Ansys Zemax | 手机镜头设计 - 第 4 部分:用 LS-DYNA 进行冲击性能分析
附件下载 联系工作人员获取附件 该系列文章将讨论智能手机镜头模组设计的挑战,从概念和设计到制造和结构变形分析。本文是四部分系列中的第四部分,它涵盖了相机镜头的显式动态模拟,以及对光学性能的影响。使用 Ansys Mechanical 和 LS - DY…...
[蓝桥杯]实现选择排序
实现选择排序 题目描述 实现选择排序算法。介绍如下: 选择排序的工作原理是每一次从需要排序的数据元素中选出最小的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排列完毕。 请编写代码,完成选择排序,…...

[蓝桥杯]卡片换位
卡片换位 题目描述 你玩过华容道的游戏吗? 这是个类似的,但更简单的游戏。 看下面 3 x 2 的格子 --------- | A | * | * | --------- | B | | * | --------- 在其中放 5 张牌,其中 A 代表关羽,B 代表张飞,* …...

【论文笔记】High-Resolution Representations for Labeling Pixels and Regions
【题目】:High-Resolution Representations for Labeling Pixels and Regions 【引用格式】:Sun K, Zhao Y, Jiang B, et al. High-resolution representations for labeling pixels and regions[J]. arXiv preprint arXiv:1904.04514, 2019. 【网址】…...

【题解-洛谷】P9422 [蓝桥杯 2023 国 B] 合并数列
题目:P9422 [蓝桥杯 2023 国 B] 合并数列 题目描述 小明发现有很多方案可以把一个很大的正整数拆成若干正整数的和。他采取了其中两种方案,分别将他们列为两个数组 { a 1 , a 2 , ⋯ a n } \{a_1, a_2, \cdots a_n\} {a1,a2,⋯an} 和 { b 1 , …...
在MATLAB中,`mean(P_train, 2)` 的含义
在MATLAB中,mean(P_train, 2) 的含义是: 计算矩阵 P_train 中每一行的平均值(沿第2个维度操作)。 详解: mean(A, dim) 函数: 对数组 A 沿维度 dim 求平均值。dim1 → 按列计算(返回行向量&…...
开源模型应用落地-OpenAI Agents SDK-集成Qwen3-8B(一)
一、前言 在人工智能技术迅猛发展的今天,OpenAI Agents SDK 为开发者提供了一个强大的工具集,用于构建基于 Python 的智能代理应用。这些代理可以执行从简单任务到复杂决策的一系列操作,极大地提升了应用程序的智能化水平。 通过 OpenAI Agents SDK,可以利用 Python 编程语…...

109页PPT华为流程模块L1-L4级梳理及研发采购服务资产5级建模
华为的流程体系是其核心竞争力之一,也是其从一家小型民营企业成长为全球领先科技巨头的重要支撑。这套体系的核心思想是以客户为中心、以价值创造为导向、以流程驱动业务、持续优化改进。 下载资料请查看文章中图片右下角信息 以下是华为流程体系的关键组成部分和特…...