当前位置: 首页 > news >正文

细说Java 引用(强、软、弱、虚)和 GC 流程(一)

一、引用概览

1.1 引用简介

JDK1.2中引入了 Reference 抽象类及其子类,来满足不同场景的 JVM 垃圾回收工作:

  • SoftReference

    • 内存不足,GC发生时,引用的对象(没有强引用时)会被清理;
    • 高速缓存使用,内存不够,就回收,如需要读取大量的本地图片;
  • WeakReference

    • GC发生时,引用的对象(没有强引用 和 软引用 时)会被清理;
    • WeakHashMapThreadLocalMap 中 key 均是弱引用;
    • jdk动态代理中缓存代理类的WeakCache;
  • PhantomReference

    • GC发生时,引用的对象(没有强引用 和 软引用时)会被清理;
    • 本质是用来跟踪被引用对象是否被 GC 回收;
    • 堆外内存(DirectByteBuffer )清理;
    • 在静态内部类中,经常会使用虚引用。例如:一个类发送网络请求,承担 callback 的静态内部类,则常以虚引用的方式来保存外部类的引用,当外部类需要被 JVM 回收时,不会因为网络请求没有及时回应,引起内存泄漏;
    • MySQL使用虚引用来解决IO资源回收问题;
    • 虚引用往往作为一种兜底策略,避免用户忘记释放资源,引发内存泄露
  • FinalReference

    • 由其唯一子类Finalizer来处理重写了 Object.finalize() 方法的实例对象,jdk 1.9 已经废弃该方法;
    • 因为必须执行重写了 Object.finalize() 的方法后才能执行GC回收实例对象,会拖慢GC速度,finalize()中操作耗时的话,GC基本无法进行;

前面三种引用我们编码时可以使用,也是我们所熟知的软引用、弱引用虚引用FinalReference 是由 JVM 使用的,下面图片显示了FinalReference 类修饰符并非public

除此而外,平常编程使用 new 创建的对象均为强引用

在这里插入图片描述

1.2 引用内存分布

1.2.1 编码示例

在这里插入图片描述

  • 虚引用创建时必须传入引用队列(ReferenceQueue),软引用和弱引用可以不传;
  • 通过Reference.get() 获取引用的对象时,虚引用永远返回 null;

1.2.2 内存分布

在这里插入图片描述

1.3 引用使用流程

1.3.1 引用生命周期

在这里插入图片描述

  • 正常使用
    0、创建对象,图示{1};
    1、创建引用队列,创建引用对象,图示{2};
    2、使用完毕后,{1} 会断开,对象没有强引用了;
  • 引用清理介入
    3、GC清理对象时,发现有引用(软、弱、虚、FinalReference);
    3.0、引用为FinalReference 时,执行步骤 4;
    3.1、内存不足,清理对象实例;内存充足时,清理弱引用和虚引用所引用的对象实例;
    3.2、如果对象实例被清理,继续往下走,否则终止;
    4、GC线程将引用对象添加到 pending 队列中,同时唤醒阻塞的 ReferenceHandler 线程;
    5、ReferenceHandler 线程消费 pending 队列;
    6、消费后的引用对象添加到用户传入的引用队列中;
    6.1、如果创建引用时没有传入引用队列就终止;
    7、用户线程消费引用队列。
ReferenceHandler 线程

Reference 类静态加载 ReferenceHandler 线程:优先级最高的守护线程
在这里插入图片描述
在这里插入图片描述

1.3.2 引用状态流转

在这里插入图片描述

1.4 GC 引用

1.4.1 引用处理原码

void ReferenceProcessor::process_discovered_references(BoolObjectClosure*           is_alive,OopClosure*                  keep_alive,VoidClosure*                 complete_gc,AbstractRefProcTaskExecutor* task_executor) {NOT_PRODUCT(verify_ok_to_handle_reflists());assert(!enqueuing_is_done(), "If here enqueuing should not be complete");// Stop treating discovered references specially.disable_discovery();bool trace_time = PrintGCDetails && PrintReferenceGC;// Soft references{TraceTime tt("SoftReference", trace_time, false, gclog_or_tty);process_discovered_reflist(_discoveredSoftRefs, _current_soft_ref_policy, true,is_alive, keep_alive, complete_gc, task_executor);}update_soft_ref_master_clock();// Weak references{TraceTime tt("WeakReference", trace_time, false, gclog_or_tty);process_discovered_reflist(_discoveredWeakRefs, NULL, true,is_alive, keep_alive, complete_gc, task_executor);}// Final references{TraceTime tt("FinalReference", trace_time, false, gclog_or_tty);process_discovered_reflist(_discoveredFinalRefs, NULL, false,is_alive, keep_alive, complete_gc, task_executor);}// Phantom references{TraceTime tt("PhantomReference", trace_time, false, gclog_or_tty);process_discovered_reflist(_discoveredPhantomRefs, NULL, false,is_alive, keep_alive, complete_gc, task_executor);}// Weak global JNI references. It would make more sense (semantically) to// traverse these simultaneously with the regular weak references above, but// that is not how the JDK1.2 specification is. See #4126360. Native code can// thus use JNI weak references to circumvent the phantom references and// resurrect a "post-mortem" object.{TraceTime tt("JNI Weak Reference", trace_time, false, gclog_or_tty);if (task_executor != NULL) {task_executor->set_single_threaded_mode();}process_phaseJNI(is_alive, keep_alive, complete_gc);}
}

1.4.2 引用GC日志

增加JVM启动参数: -XX:+PrintReferenceGC -XX:+PrintGCDetails, 打印各种引用对象的详细回收时间。

涉及系统存在大量引用回收时,GC耗时显著增加,可以通过增加参数 -XX:+ParallelRefProcEnabled 开启 ( JDK8版本默认关闭的,在JDK9+之后默认开启 ) 并行处理引用来快速优化GC,具体根因后面可以继续分析。

日志样例如下:
2023-06-04T10:28:52.886+0800: 24397.548: [GC concurrent-root-region-scan-start]:开始扫描并发根区域。
2023-06-04T10:28:52.941+0800: 24397.602: [GC concurrent-root-region-scan-end, 0.0545027 secs]:并发根区域扫描结束,持续时间为0.0545027秒。
2023-06-04T10:28:52.941+0800: 24397.602: [GC concurrent-mark-start]:开始并发标记过程。
2023-06-04T10:28:53.198+0800: 24397.859: [GC concurrent-mark-end, 0.2565503 secs]:并发标记过程结束,持续时间为0.2565503秒。
2023-06-04T10:28:53.199+0800: 24397.860: [GC remark]: G1执行remark阶段。
2023-06-04T10:28:53.199+0800: 24397.860: [Finalize Marking, 0.0004169 secs]:标记finalize队列中待处理对象,持续时间为0.0004169秒。
2023-06-04T10:28:53.199+0800: 24397.861: [GC ref-proc]: 进行引用处理。
2023-06-04T10:28:53.199+0800: 24397.861: [SoftReference, 9247 refs, 0.0035753 secs]:处理软引用,持续时间为0.0035753秒。
2023-06-04T10:28:53.203+0800: 24397.864: [WeakReference, 963 refs, 0.0003121 secs]:处理弱引用,持续时间为0.0003121秒。
2023-06-04T10:28:53.203+0800: 24397.865: [FinalReference, 60971 refs, 0.0693649 secs]:处理虚引用,持续时间为0.0693649秒。
2023-06-04T10:28:53.273+0800: 24397.934: [PhantomReference, 49828 refs, 20 refs, 4.5339260 secs]:处理final reference中的phantom引用,持续时间为4.5339260秒。
2023-06-04T10:28:57.807+0800: 24402.468: [JNI Weak Reference, 0.0000755 secs]:处理JNI weak引用,持续时间为0.0000755秒。
2023-06-04T10:28:57.821+0800: 24402.482: [Unloading, 0.0332897 secs]:卸载无用的类,持续时间为0.0332897秒。
[Times: user=4.60 sys=0.31, real=4.67 secs]:垃圾回收的时间信息,user表示用户态CPU时间、sys表示内核态CPU时间、real表示实际运行时间。
2023-06-04T10:28:57.863+0800: 24402.524: [GC cleanup 4850M->4850M(9984M), 0.0031413 secs]:执行cleanup操作,将堆大小从4850M调整为4850M,持续时间为0.0031413秒。

1.5 案例分析

1.5.0 高速缓存

1.5.1 WeakHashMap

  • WeakHashMap

    线程不安全, 通过 Collections.synchronizedMap来生成一个线程安全的map

public class WeakHashMap<K,V>extends AbstractMap<K,V>implements Map<K,V> {//……Entry<K,V>[] table;/*** Reference queue for cleared WeakEntries*/private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
}
  • WeakHashMap.Entry

    1、弱引用对象;
    2、传递了引用队列;
    3、没有引用队列消费线程, 通过WeakHashMap中大多数方法(get()put()size()等)来消费引用队列,从而释放Entry

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {V value;final int hash;Entry<K,V> next;/*** Creates new entry.*/Entry(Object key, V value,ReferenceQueue<Object> queue, int hash, Entry<K,V> next) {super(key, queue);this.value = value;this.hash  = hash;this.next  = next;}
1.5.1.2 WeakhashMap使用场景
  • 缓存系统:Tomcat的工具类里的 ConcurrentCache
package org.apache.tomcat.util.collections;import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;public final class ConcurrentCache<K,V> {private final int size;private final Map<K,V> eden;private final Map<K,V> longterm;public ConcurrentCache(int size) {this.size = size;this.eden = new ConcurrentHashMap<>(size);this.longterm = new WeakHashMap<>(size);}public V get(K k) {V v = this.eden.get(k);if (v == null) {synchronized (longterm) {v = this.longterm.get(k);}if (v != null) {this.eden.put(k, v);}}return v;}public void put(K k, V v) {if (this.eden.size() >= size) {synchronized (longterm) {this.longterm.putAll(this.eden);}this.eden.clear();}this.eden.put(k, v);}
}
  • 诊断工具:在阿里开源的Java诊断工具Arthas中使用了WeakHashMap做类-字节码的缓存。
/**
* 类-字节码缓存
* Class: Class
* byte[]: bytes of Class
**/ 
private final static Map<Class<?>, byte[]> classBytesCache = new WeakHashMap<>();

1.5.2 ThreadLocal

1.5.2.1 ThreadLocal 内存分配图如下所示

在这里插入图片描述

1.5.2.2 ThreadLocal 涉及对象及垃圾回收过程
  • ThreadLocalMap

    1、ThreadLocalMap 是 Thread 内部的成员变量,即图示{0}为强引用;
    2、ThreadLocalMap 是一个map,key是 WeakReference<ThreadLocal<?>>,即 key 是一个弱引用对象,图示{2};value 是一个强引用,图示{3};
    3、当Thread 销毁之后对应的 ThreadLocalMap 也就随之销毁;

  • ThreadLocalMap.Entry

    1、弱引用对象;
    2、没有传递引用队列;

	static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}
  • ThreadLocal

    • 正常使用
      0、创建ThreadLocal对象,即图示{1};
      1、通过方法set(obj) 时会将值 obj 填充进当前线程的ThreadLocalMap,即图示{2}、{3};
    • 弱引用清理介入
      2、使用完毕后,{1} 断开;
      3、GC时发现ThreadLocal对象只存在弱引用,可以直接回收ThreadLocal对象;
1.5.2.3 ThreadLocal 思考
  • ThreadLocalMap 的 key 是强引用不可以吗?

    答:当线程一直存活时(实际使用时会用线程池,线程大概率不会销毁),图示{2}为强引用时,垃圾ThreadLocal对象无法回收,造成内存泄漏;

  • ThreadLocalMap 的 value 为啥不设计为弱引用?

    答:当 value 只有弱引用时,GC时会直接回收value,当使用key来获取value时,value返回null,这显然有问题;

  • ThreadLocalMap 什么情况下会导致导致内存泄漏?

    答:当线程一直存活时,key 因为是弱引用,GC会回收,但ThreadLocalMap的 value 却是强引用,会阻止GC回收;

    时间一长,ThreadLocalMap会存在很多 key 为 null,value 不为 null的情况;
    这种情况调用ThreadLocal.get()ThreadLocal.set()ThreadLocal.remove()有概率(遇到hash冲突) 将之前的 key 为 null 的 entry 清理;

    所以,使用完毕ThreadLocal,一定要记得执行remove()方法。

    综上,使用完ThreadLocalThread依然运行的前提下,就算忘记调用remove方法,弱引用比强引用可以多一层保障:弱引用的ThreadLocal会被回收,对应的value在下一次ThreadLocal调用set,get,remove中的任一方法的时候会被清除,从而避免内存泄漏。

  • ThreadLocal 的正确使用姿势

    • 定义为类的静态变量 private static final ThreadLocal<Integer>,如官方文档所示,通过静态方法ThreadId.get() 来使用,这样使得 key 永远存活(ThreadLocal实例在内存只有一份);
    • 使用完毕后通过实例方法ThreadLocal.remove() 来移除 Entry,从而避免ThreadLocalMap中的value 产生内存泄漏。
      在这里插入图片描述
  • ThreadLocalMap 内部Hash冲突使用的是线性探测,并非HashMap的拉链法。

    HashMap 是性能优先,尽可能的保证元素的高效访问。ThreadLocalMap 性能不是第一要素;如果数组元素比较密集的话,ThreadLocalMap 不管是 set 还是 get 都会不可避免地扫描很多节点,这肯定会影响性能。但是换来的收益,就是 ThreadLocalMap 可以在扫描节点时主动发现过期节点(key 为 null )且清理掉,尽可能的避免内存泄漏。

    ThreadLocal 中一个属性 HASH_INCREMENT = 0x61c88647 ,0x61c88647 是斐波那契数 也叫 黄金分割数。hash增量为这个数字,使得 hash 码能均匀的分布在2的N次方的数组里, 即 Entry[] table,所以ThreadLocalMap 中的散列值分散的十分均匀,很少会出现冲突;

    ThreadLocal 往往存放的数据量不会特别大,而且 key 是弱引用又会被垃圾回收,所以,线性探测法会查询更快,同时也更省空间。

1.5.2.4 ThreadLocal 使用场景
  • HikariPool 数据库连接池高性能原因之一:

    连接池从连接池中获取连接时对于同一个线程在ThreadLocal中添加了缓存,同一线程获取连接时没有并发操作。

  • 全局 Token 管理:自定义拦截器,把Token放入ThreadLocal 后续通过ThreadLocal ,获取用户信息;

  • org.slf4j.MDC(Mapped Diagnostic Context) 去埋点TraceId,跟踪多个服务调用,巧妙实现链路跟踪(MDC 底层依赖ThreadLocal);

  • 不同层数据库连接读取,用于完成一个事务;

  • ThreadLocal 用于同一个线程内,对于父子线程使用InheritableThreadLocal,线程池使用阿里巴巴的TransmittableThreadLocal组件

1.5.3 堆外内存(DirectByteBuffer )回收

  • HeapByteBuffer 在堆内存分配;

    ByteBuffer.allocate();

  • DirectByteBuffer 在堆外分配;

    ByteBuffer.allocateDirect();

1.5.3.1 DirectByteBuffer 内存分配图如下所示

在这里插入图片描述

1.5.3.2 DirectByteBuffer 涉及对象及堆外内存释放过程
  • Deallocator

    0、是一个 Runnable 对象;
    1、记录了堆外内存地址、大小;
    2、负责清理堆外内存。

  • Cleaner

    0、是一个虚引用(PhantomReference)对象
    1、ReferenceHandler 线程(Reference 类加载时会创建)触发清理;
    2、内部有 前驱和后继,方便组成双向链表(图示线路{5}),链表头 first 是Cleaner 类的静态变量;避免在 DirectByteBuffer 对象前被GC;
    3、线程安全。

  • DirectByteBuffer

    • 正常使用
      0、记录了堆外内存地址、大小;
      1、正常使用时通过图示线路{1}、{2}读写堆外内存;
      2、使用完毕后,{1} 会断开,变成不可达对象;
    • 虚引用清理介入
      3、GC 发现 DirectByteBuffer 对象有虚引用{4},同时清理 DirectByteBuffer 对象,断开{3},{4};
      4、GC 线程将虚引用对象 Cleaner 放入到 pending 队列,同时唤醒ReferenceHandler线程;
      5、ReferenceHandler 线程消费 pending 队列,拿到 Cleaner 对象后,调用Cleaner.clean()方法;
      6、Cleaner.clean()首先断开{5},然后内部调用 Deallocator.run() 方法清理堆外内存(依赖unsafe.freeMemory());
      7、Cleaner 对象和Deallocator 在后续GC时可以回收;
    • ReferenceHandler 线程 优先级最高(可查阅 1.3.1 引用生命周期处代码片段),只要GC触发后,就可以释放堆外内存;
    • 尴尬的是,GC时机不确定,那堆外内存释放的时间也不确定了?
    • 不怕,下一次分配堆外内存时,发现内存不足,会触发System.gc()
    • 所以,下一次分配是啥时候呢?不确定,哈哈。

1.5.4 FinalReference 回收

1.5.4.1 FinalReference 内存分配图如下所示

在这里插入图片描述

  • Finalizer

    • FinalReference 唯一的子类,用于执行 重写的java.lang.Object.finalize()方法;
1.5.4.2 Finalizer 内存释放过程
  • 正常创建对象

    1、创建对象(该对象覆写了方法:Object.finalize() ,记为FinalReferenceObj),图示{1};
    2、JVM 将该对象注册到Finalizer上,即创建 FinalReference 引用的对象 Finalizer,引用指向之前创建的对象,图示{2};
    3、同时,将Finalizer 实例对象加入到 unfinalized 链表中, 图示{7};

  • FinalReference 引用介入释放

    4、FinalReferenceObj 使用完毕,断开{1};
    5、GC时发现对象FinalReferenceObjFinalReference引用,暂停回收FinalReferenceObj对象;
    6、gc线程将Finalizer 对象放入 pending 队列;
    7、ReferenceHandler线程消费 pending 队列,取出Finalizer 对象,加入到引用队列中;
    8、FinalizerThread线程消费引用队列,取出Finalizer 对象,找到FinalReferenceObj 对象,执行其覆写的方法:Object.finalize()
    9、断开 FinalReference 引用,图示{2},后续GC可以回收FinalReferenceObj 对象;
    10、从 unfinalized 链表移除Finalizer 对象,图示{7};后续GC可以回收Finalizer 对象;

    FinalizerThread线程 优先级较低(可以查阅1.5.4.3 Finalizer代码简析 ),所以执行 finalize() 方法会延迟(如果finalize()方法内部也有耗时操作,那就是雪上加霜了),导致最终累积大量垃圾,造成GC耗时,拖垮系统。

1.5.4.3 Finalizer代码简析
final class Finalizer extends FinalReference<Object> {// …… // 1.0 unfinalized实际上是一个双向链表,在add方法被调用后,就会将当前对象加入到unfinalized链表。// 2.0 当前创建对象在虚拟机内仅该unfinalized链表持有一份引用// 3.0 当执行完重写的java.lang.Object#finalize方法后,才会重列表移除;避免FinalReference 引用在实例对象前被GC; // 这里会拖垮GC效率,发现GC完了一次,压根没有释放内存// 4.0 本质就是给GC的对象一个强引用private static Finalizer unfinalized = null;private Finalizer next, prev;private Finalizer(Object finalizee) {super(finalizee, queue);add(); // 将当前对象加入到unfinalized链表。}/*** register方法仅会被虚拟机所调用,而且,只有重写了java.lang.Object#finalize方法的类才会被作为参数调用Finalizer#register方法。**//* Invoked by VM */static void register(Object finalizee) {new Finalizer(finalizee);}// 启动 FinalizerThread 线程消费引用队列里的FinalReference,// 就是执行重写的java.lang.Object#finalize方法,然后从unfinalized 队列中移除,方便 GC。static {// ……Thread finalizer = new FinalizerThread(tg);// 该线程的优先级并不能保证finalizer.setPriority(Thread.MAX_PRIORITY - 2); finalizer.setDaemon(true);finalizer.start();}// 自定义引用队列private static ReferenceQueue<Object> queue = new ReferenceQueue<>();// ……/*** 执行obj.finalize()方法**/private void runFinalizer(JavaLangAccess jla) {// ……try {// 拿到覆写finalize()方法的对象,再次建立强引用Object finalizee = this.get();assert finalizee != null;if (!(finalizee instanceof java.lang.Enum)) {// 执行obj.finalize()方法jla.invokeFinalize(finalizee);// 将刚刚的强引用释放;finalizee = null;}} catch (Throwable x) { }// 解除 FinalReferencesuper.clear();}// ……
}
1.5.4.3 Java 程序启动时的线程

java程序启动时就有 finalizer 线程(FinalizerThread)、ReferenceHandler 线程,如下图示:

在这里插入图片描述

二、引用堆积引发GC耗时血案

  • RPC 使用短连接调用,导致 Socket 的 FinalReference 引用较多,致使 YoungGC 耗时较长

    • rpc项目中的长连接与短连接的思考

    解决:
    1、增加参数-XX:+ParallelRefProcEnabled 可以缓解;
    2、通过将短连接改成长连接,减少了 Socket 对象的创建,从而减少 FinalReference,来降低 YoungGC 耗时。

  • Mysql连接断开兜底策略使用ConnectionPhantomReference,导致大量PhantomReference 堆积,引起GC耗时严重

    • 案例一
    • 案例二
    • 案例三
    • 案例四

    解决:
    1、增加参数-XX:+ParallelRefProcEnabled 可以缓解;
    2、升级MySQL jdbc driver到8.0.22+,开启disableAbandonedConnectionCleanup 可以根治;

相关文章:

细说Java 引用(强、软、弱、虚)和 GC 流程(一)

一、引用概览 1.1 引用简介 JDK1.2中引入了 Reference 抽象类及其子类&#xff0c;来满足不同场景的 JVM 垃圾回收工作&#xff1a; SoftReference 内存不足&#xff0c;GC发生时&#xff0c;引用的对象&#xff08;没有强引用时&#xff09;会被清理&#xff1b;高速缓存使用…...

C++,设计模式,【工厂方法模式】

文章目录 如何用汽车生产线理解工厂方法模式?一、传统生产方式的困境二、工厂方法模式解决方案三、模式应用场景四、模式优势分析五、现实应用启示✅C++,设计模式,【目录篇】 如何用汽车生产线理解工厂方法模式? 某个早晨,某车企CEO看着会议室里堆积如面的新车订单皱起眉…...

分布式之分布式ID

目录 需求 1. 全局唯一性 2. 高性能 3. 高可用性 4. 可扩展性 5. 有序性 6. 时间相关 7. 长度适中 8. 安全性 9. 分布式一致性 10. 易于集成 常见解决方案 选择依据 数据库号段模式 核心概念 工作流程 优点 缺点 实现示例 优化策略 适用场景 Snowflake雪…...

Innovus中快速获取timing path逻辑深度的golden脚本

在实际项目中我们经常会遇到一条timing path级数特别多&#xff0c;可能是一两页都翻不完。此时&#xff0c;我们大都需要手工去数这条path上到底有哪些是设计本身的逻辑&#xff0c;哪些是PR工具插入的buffer和inverter。 数字IC后端手把手培训教程 | Clock Gating相关clock …...

tortoiseGit的使用和上传拉取

tortoiseGit的使用和上传拉取 下载TortoiseGit 通过网盘分享的文件&#xff1a;tortoiseGit.zip 链接: https://pan.baidu.com/s/1EOT_UsM9_OysRqXa8gES4A?pwd1234 提取码: 1234 在电脑桌面新建文件夹并进入 右击鼠标 将网址复制上去 用户名和密码是在git注册的用户名和…...

简单工厂模式 (Simple Factory Pattern) 在Spring Boot 中的应用

简单工厂模式&#xff08;Simple Factory Pattern&#xff09;虽然不属于 GoF 23 种经典设计模式&#xff0c;但在实际开发中非常常用&#xff0c;尤其是在 Spring Boot 项目中。它提供了一种简单的方式来创建对象&#xff0c;将对象的创建逻辑集中到一个工厂类中。 一、简单工…...

前端排序算法完全指南:从理论到实践

<!DOCTYPE html> <html> <head><title>前端排序算法终极指南</title><style>.container { max-width: 1000px; margin: 0 auto; padding: 20px; }.demo-container { margin: 30px 0; border: 1px solid #eee; padding: 20px; }.bars-wrapp…...

【LeetCode Hot100 矩阵】矩阵置零、螺旋矩阵、旋转图像、搜索二维矩阵II

矩阵 1. 矩阵置零&#xff08;Set Matrix Zeroes&#xff09;解题思路步骤&#xff1a; 代码实现 2. 螺旋矩阵&#xff08;Spiral Matrix&#xff09;解题思路具体步骤&#xff1a; 代码实现 3. 旋转矩阵 90 度解决思路代码实现 5. 搜索二维矩阵中的目标值解决思路代码实现 1. …...

最新版IDEA下载安装教程

一、下载IDEA 点击前往官网下载 或者去网盘下载 点击前往百度网盘下载 点击前往夸克网盘下载 进去后点击IDEA 然后点击Download 选择自己电脑对应的系统 点击下载 等待下载即可 二、安装IDEA 下载好后双击应用程序 点击下一步 选择好安装目录后点击下一步 勾选这两项后点击…...

Embedding模型

检索的方式有那些 关键字搜索&#xff1a;通过用户输入的关键字来查找文本数据。 语义搜索&#xff1a;它的目标是理解用户查询的真实意图&#xff0c;不仅考虑关键词的匹配&#xff0c;还考虑词汇之间的语义 &#xff08;文字&#xff0c;语音&#xff0c;语调...&#xff0…...

WSL进阶使用指南

WSL2通过 Hyper-V 技术创建了一个轻量级的虚拟机&#xff08;VM&#xff09;&#xff0c;在这个虚拟机之上可以运行一个真正的 Linux 内核&#xff0c;这给希望同时使用 Windows 和 Linux 的开发人员提供了无缝高效的体验。本文会介绍一些使用WSL的知识&#xff0c;帮助你更好地…...

JavaScript函数-函数的参数

在JavaScript编程语言中&#xff0c;函数是组织代码和实现复杂逻辑的基本单元。而函数参数则是这些功能的重要组成部分&#xff0c;它们允许我们将数据传递给函数&#xff0c;从而使得函数更加通用和灵活。本文将深入探讨JavaScript函数参数的各种特性及其最佳实践。 参数基础…...

【C语言】第五期——函数

目录 0 前言 1 定义函数 2 调用函数 3 函数的实参和形参 4 函数声明 5 作用域 5.1 局部变量和全局变量 5.2 static关键字 5.2.1 修饰局部变量 5.2.2 修饰全局变量 5.2.3 修饰函数 6 函数的返回值 6.1 return语句 6.2 函数返回值的类型 7 函数的其他形式 7.1 函…...

线结构光三维重建

利用线结构光和单目进行三维重构&#xff08;测距&#xff09;_线结构光三维重建-CSDN博客...

Spring Boot 应用(官网文档解读)

Spring Boot 启动方式 SpringApplication.run(MyApplication.class, args); Spring Boot 故障分析器 在Spring Boot 项目启动发生错误的时候&#xff0c;我们通常可以看到上面的内容&#xff0c;即 APPLICATION FAILED TO START&#xff0c;以及后面的错误描述。这个功能是通过…...

基于ffmpeg+openGL ES实现的视频编辑工具-添加转场(九)

在视频编辑的广阔领域中,转场效果无疑是提升视频流畅性与观赏性的关键要素。巧妙运用转场,能够让不同视频片段之间的衔接更为自然,同时赋予视频独特的创意魅力。本文将深入探讨如何借助 ffmpeg 和 openGL ES 技术,在视频编辑工具中实现丰富多样的转场效果。 一、转场技术原…...

库的制作与原理(一)

1.库的概念 库是写好的&#xff0c;现成的可以复用的代码。本质上库是一种可执行的二进制形式&#xff0c;可以被操作系统载入内存执行。库有俩种&#xff1a;静态库 .a[Linux] .lib[windows] 动态库 .so[Linux] .dll[windows] 就是把.c文件变成.o文件&#xff0c;把…...

Java List 自定义对象排序 Java 8 及以上版本使用 Stream API

从 Java 8 开始&#xff0c;你可以使用 Stream API 对 List 进行排序&#xff0c;这种方式更加简洁和灵活。 以下是一个示例代码&#xff1a; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors;// 自定…...

单元测试的策略有哪些,主要包括什么?

单元测试的策略及主要内容 单元测试&#xff08;Unit Testing&#xff09;是指对软件系统中的最小可测试单元&#xff08;通常是一个函数、方法或类&#xff09;进行验证&#xff0c;以确保其行为符合预期。常见的单元测试策略可以分为基于代码的策略和基于数据的策略&#xf…...

《深度剖析:AI与姿态估计技术在元宇宙VR交互中的应用困境》

在元宇宙的宏大版图里&#xff0c;虚拟现实&#xff08;VR&#xff09;交互是构建沉浸式体验的关键支柱&#xff0c;而人工智能&#xff08;AI&#xff09;与姿态估计技术的融合&#xff0c;本应成为提升交互体验的强大引擎。但在实际应用中&#xff0c;它们面临着诸多复杂且棘…...

基于YOLO11深度学习的糖尿病视网膜病变检测与诊断系统【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...

【QT 网络编程】HTTP协议(二)

文章目录 &#x1f31f;1.概述&#x1f31f;2.代码结构概览&#x1f31f;3.代码解析&#x1f338;Http_Api_Manager - API管理类&#x1f338;Http_Request_Manager- HTTP请求管理类&#x1f338;ThreadPool - 线程池&#x1f338;TestWindow- 测试类 &#x1f31f;4.运行效果&…...

mysql之规则优化器RBO

文章目录 MySQL 基于规则的优化 (RBO)&#xff1a;RBO 的核心思想&#xff1a;模式匹配与规则应用RBO 的主要优化规则查询重写 (Query Rewrite) / 查询转换 (Query Transformation)子查询优化 (Subquery Optimization) - RBO 的重中之重非相关子查询 (Non-Correlated Subquery)…...

Python天梯赛10分题-念数字、求整数段和、比较大小、计算阶乘和

007-念数字 输入一个整数&#xff0c;输出每个数字对应的拼音。当整数为负数时&#xff0c;先输出fu字。十个数字对应的拼音如下&#xff1a; 0: ling 1: yi 2: er 3: san 4: si 5: wu 6: liu 7: qi 8: ba 9: jiu输入格式&#xff1a; 输入在一行中给出一个整数&#xff0c;如&…...

如何进行文档类图像的校正?

可以使用OpenCV实现的图像校正算法&#xff0c;包含透视校正和旋转校正的步骤&#xff0c;并附有详细注释。 具体如下&#xff1a; import cv2 import numpy as npdef order_points(pts):"""将四个点按左上、右上、右下、左下顺序排列"""rect …...

GPIO外设

一、GPIO简介 GPIO&#xff0c;general-purpos IO port,通用输入输出引脚&#xff0c;所有的GPIO引脚都有基本的输入输出功能。 最基本的输出功能&#xff1a;STM32控制引脚输出高、低电平&#xff0c;实现开关控制&#xff1b;最基本的输入功能&#xff1a;检测外部输入电平&…...

DeepSeek-R1之二_基于Open-WebUI的AI托管平台之Pyenv-win安装与配置搭建本地AI知识库

DeepSeek-R1之二_基于Open-WebUI的AI托管平台之Pyenv-win安装与配置搭建本地AI知识库 文章目录 DeepSeek-R1之二_基于Open-WebUI的AI托管平台之Pyenv-win安装与配置搭建本地AI知识库1. 官网及前提条件1. 官网2. 前提条件1. 安装了Ollama2. 通过Ollama下载与管理了DeepSeek-R1模…...

My Metronome for Mac v1.4.2 我的节拍器 支持M、Intel芯片

应用介绍 My Metronome 是一款适用于 macOS 的专业节拍器应用程序&#xff0c;旨在帮助音乐家、作曲家、学生和任何需要精确节奏控制的人进行练习。无论是进行乐器练习、音乐创作还是演出排练&#xff0c;My Metronome 都能为用户提供精准的节拍支持和灵活的功能&#xff0c;确…...

Windows系统本地部署DeepSeek-R1+本地知识库+联网搜索+Agent功能

本文记录了Windows11 Ollama AnythingLLM&#xff0c;3步快速本地部署DeepSeek-R1模型&#xff0c;支持联网搜索、应用本地知识库和创建Agent功能。 前言 DeepSeek-R1 知识库相关 更新时间&#xff1a;截至 2025年2月&#xff0c;当前版本的 R1 基于 2024年7月之前的数据训…...

RT-Thread+STM32L475VET6——TF 卡文件系统

文章目录 前言一、板载资源二、具体步骤1.打开CubeMX进行USB配置1.1 使用外部高速时钟&#xff0c;并修改时钟树1.2 打开SPI1&#xff0c;参数默认即可(SPI根据自己需求调整&#xff09;1.3 打开串口&#xff0c;参数默认1.4 生成工程 2.配置SPI2.1 打开SPI驱动2.2 声明使用SPI…...

Jmeter进阶篇(34)如何解决jmeter.save.saveservice.timestamp_format=ms报错?

问题描述 今天使用Jmeter完成压测执行,然后使用命令将jtl文件转换成html报告时,遇到了报错! 大致就是说jmeter里定义了一个jmeter.save.saveservice.timestamp_format=ms的时间格式,但是jtl文件中的时间格式不是标准的这个ms格式,导致无法正常解析。对于这个问题,有如下…...

Javascript使用Sodium库实现 aead_xchacha20poly1305_ietf加密解密,以及与后端的密文交互

Node.js环境安装 sodium-native (其他库可能会出现加密解密失败&#xff0c;如果要使用不一样的库&#xff0c;请自行验证) npm install sodium-native 示例代码&#xff0c;使用的是 sodium-native v4.3.2 (其他版本可能会有变化&#xff0c;如果要使用&#xff0c;请自行验…...

机器学习实战(8):降维技术——主成分分析(PCA)

第8集&#xff1a;降维技术——主成分分析&#xff08;PCA&#xff09; 在机器学习中&#xff0c;降维&#xff08;Dimensionality Reduction&#xff09; 是一种重要的数据处理技术&#xff0c;用于减少特征维度、去除噪声并提高模型效率。主成分分析&#xff08;Principal C…...

0099__Visual Studio 引入外部静态库与动态库

Visual Studio 引入外部静态库与动态库_visual studio 添加库-CSDN博客...

eclips 快捷键

eclips 快捷键 类别快捷键功能描述通用Ctrl S保存当前文件Ctrl Shift S保存所有文件Ctrl Z撤销操作Ctrl Y重做操作Ctrl X剪切Ctrl C复制Ctrl V粘贴Ctrl A全选Ctrl F查找Ctrl H打开搜索对话框Ctrl /注释/取消注释当前行或选中的代码块Ctrl Shift /添加块注释Ctrl …...

VSCode ssh远程连接内网服务器(不能上网的内网环境的Linux服务器)的终极解决方案

VSCode ssh远程连接内网服务器&#xff08;不能上网的内网环境的Linux服务器&#xff09; 离线下载vscode-server并安装: 如果远程端不能联网可以下载包离线安装,下载 vscode-server 的 url 需要和 vscode 客户端版本的 commit-id 对应.通过 vscode 面板的帮助->关于可以获…...

【Gin-Web】Bluebell社区项目梳理3:社区相关接口开发

本文目录 一、接口详情1. 获取分类社区列表接口2. 根据id查询社区 二、值类型与引用类型 一、接口详情 跟社区有关的接口详情如下。 1. 获取分类社区列表接口 首先是Controller层&#xff0c;然后跳转到Logic层业务逻辑的开发。 这是Logic层&#xff0c;再做一次跳转&#…...

鸟语林-论坛系统自动化测试

文章目录 一、自动化实施步骤1.1编写Web测试用例1.2 编写自动化代码1.2.1 LoginPageTest1) 能否正确打开登录页面2) 点击去注册能否跳转注册页面3) 模拟用户登录&#xff0c;输入多组登录测试用例 1.2.2 RegisterPageTest1) 能否成功打开注册页面2) 注册测试用例3) 点击去登录按…...

图解循环神经网络(RNN)

目录 1.循环神经网络介绍 2.网络结构 3.结构分类 4.模型工作原理 5.模型工作示例 6.总结 1.循环神经网络介绍 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;是一种专门用于处理序列数据的神经网络结构。与传统的神经网络不同&#xff0c…...

c语言左值和右值的区别

在C语言中&#xff0c;左值&#xff08;lvalue&#xff09;和右值&#xff08;rvalue&#xff09;是互斥的概念&#xff0c;左值不能是右值。以下是详细的解释和总结&#xff1a; 1. 左值&#xff08;lvalue&#xff09; 定义&#xff1a;左值是一个表达式&#xff0c;表示一个…...

Scrapy:Downloader下载器设计详解

Scrapy下载器设计详解 1. 整体架构 Scrapy的下载器(Downloader)是整个爬虫框架的核心组件之一&#xff0c;负责处理所有网络请求的下载工作。它的主要职责是&#xff1a; 管理并发请求实现请求调度处理下载延迟维护下载槽(Slot) 官方文档&#xff1a;Settings中的Downloader配…...

细说STM32F407单片机2个ADC使用DMA同步采集各自的1个输入通道的方法

目录 一、示例说明 二、工程配置 1、RCC、DEBUG、CodeGenerator 2、USART6 3、TIM3 &#xff08;1&#xff09;Mode &#xff08;2&#xff09;参数设置 &#xff08;3&#xff09; TRGO &#xff08;4&#xff09;ADC1_IN0 1&#xff09;ADCs_Common_Settings 2&a…...

【分治法】线性时间选择问题

问题描述 给定线性序列中n个元素和一个整数k&#xff0c;1≤k≤n&#xff0c;要求在线性时间中找出这n个元素中第k小的元素 常规思路 常规思路是对序列先排序&#xff0c;落在第k个位置的元素就是第k小的元素。 这种方法的时间复杂度不是线性的&#xff0c;是O(nlogn)的时间…...

redis中的Lua脚本,redis的事务机制

lua脚本的特点 lua脚本可以操作redis数据库&#xff0c;并且脚本中的代码满足原子性&#xff0c;要么全部被执行&#xff0c;要么全部不执行 lua脚本的语法 脚本示例 lua脚本的草稿&#xff1a; 最终的lua脚本 lua脚本在java里调用的方法 RedisTemplete类里有一个方法&…...

ASUS/华硕 ROG Strix GL503VM 原厂Win10系统 工厂文件 带ASUS Recovery恢复

华硕工厂文件恢复系统 &#xff0c;安装结束后带隐藏分区&#xff0c;带一键恢复&#xff0c;以及机器所有的驱动和软件。 支持型号&#xff1a;GL503VM 系统版本&#xff1a;Windows 10 文件下载&#xff1a;点击下载 文件格式&#xff1a;工厂文件 安装教程&#xff1a;…...

Oracle 深入理解Lock和Latch ,解析访问数据块全流程

Oracle 锁机制介绍 根据保护对象的不同&#xff0c;单实例Oracle数据库锁可以分为以下几大类&#xff1a; DML lock&#xff08;data locks&#xff0c;数据锁&#xff09;&#xff1a;用于保护数据的完整性&#xff1b; DDL lock&#xff08;dictionary locks&#xff0c;字典…...

Django Admin: 动态合并数据库和预定义选项的高级过滤器实现

在 Django 管理界面中,我们经常需要为某些字段提供过滤选项。通常情况下,这些选项要么是预定义的,要么是从数据库中动态获取的。但是,有时我们需要更灵活的解决方案:当数据库为空时使用预定义选项,而当数据库有数据时,则合并预定义选项和数据库中的值。本文将详细介绍如…...

Linux文件系统

理解硬件 磁盘、服务器、机柜、机房 机械磁盘是计算机中唯一的一个机械设备 磁盘--- 外设&#xff0c;慢&#xff0c;容量大&#xff0c;价格便宜 磁盘物理结构 扇区是从磁盘读出和写入信息的最小单位&#xff0c;通常大小为 512 字节。磁头&#xff08;head&#xff09;数&a…...

C++标准库——时间

文章目录 chrono库durationtime_pointclocks C 风格日期和时间库参考 C 支持两种类型的时间操作&#xff1a; Chrono库&#xff0c;在chrono头文件中提供C 风格日期和时间库&#xff0c;std::time这种&#xff0c;在ctime头文件中提供 chrono库 在<chrono>中&#xff0…...

AutoGen 技术博客系列 八:深入剖析 Swarm—— 智能体协作的新范式

本系列博文在掘金同步发布, 更多优质文章&#xff0c;请关注本人掘金账号&#xff1a; 人肉推土机的掘金账号 AutoGen系列一&#xff1a;基础介绍与入门教程 AutoGen系列二&#xff1a;深入自定义智能体 AutoGen系列三&#xff1a;内置智能体的应用与实战 AutoGen系列四&am…...