细说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 可以根治;

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

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

相关文章

win11系统无法打开软件_组策略无法打开_gpedit.msc不生效_为了对电脑进行保护,已经阻止此应用---Windows工作笔记057

碰到这个问题挺麻烦的,要用的软件打不开了. 其实解决方法就是去组策略中修改一个策略就可以了,但是: 先来说: 而且,使用cmd输入的gpedit.msc也打不开了. 这个怎么解决? @echo off pushd "%~dp0"dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPo…

算法日记23:SC16+17(求数的因子+质因子)

题目1&#xff1a; 求解因子 题解1&#xff1a; 1&#xff09;易得&#xff0c;当 n a ∗ b na*b na∗b时&#xff0c; a , b {a,b} a,b是n的因子(假设 a < b a<b a<b) 可以发现只需枚举到即可 n \sqrt{n} n ​&#xff0c;因为 a < n < b a<\sqrt{n}&l…

欢乐力扣:同构字符串

文章目录 1、题目描述2、 代码 1、题目描述 同构字符串。给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那么这两个字符串是同构的。  每个出现的字符都应当映射到另一个字符&#xff0c;同时不改变字符…

【HeadFirst系列之HeadFirst设计模式】第7天之命令模式:封装请求,轻松实现解耦!

命令模式&#xff1a;封装请求&#xff0c;轻松实现解耦&#xff01; 大家好&#xff01;今天我们来聊聊设计模式中的命令模式&#xff08;Command Pattern&#xff09;。如果你曾经需要将请求封装成对象&#xff0c;或者希望实现请求的撤销、重做等功能&#xff0c;那么命令模…

敏捷开发07:敏捷项目可视化管理-ScrumBoard(Scrum板)使用介绍

ScrumBoard(Scrum板)介绍 ScrumBoard&#xff08;Scrum板&#xff09;是敏捷项目管理中使用的可视化工具&#xff0c;用于跟踪和监控冲刺阶段的任务进度。 主要通过可视化的看板来管理工作&#xff0c;它可视化了敏捷开发中的工作流程、任务状态、团队角色。 Scrum 团队在各…

Linux第十三节 — 进程状态详解

只要一个进程的PCB还存在内存当中&#xff0c;哪怕此时该进程对应的代码和数据已经在磁盘当中&#xff0c;此时依然认为该进程仍然存在&#xff01; 一、Linux进程的运行状态R 接下来我们看下面这个例子&#xff1a; 当我们执行这个程序的时候&#xff0c;我们认为该进程的状…

无人机遥控器接口作用详解!

USB接口&#xff1a; 功能&#xff1a;USB接口是一种通用串行总线接口&#xff0c;用于连接外部设备&#xff0c;如手机、平板、电脑或充电设备。在无人机遥控器上&#xff0c;USB接口通常用于数据传输和充电。 应用&#xff1a;用户可以通过USB接口将遥控器与电脑连接&#…

SVN把英文换中文

原文链接&#xff1a;SVN设置成中文版本 都是英文&#xff0c;换中文 Tortoise SVN 安装汉化教程(乌龟SVN) https://pan.quark.cn/s/cb6f2eee3f90 下载中文包

云手机如何进行经纬度修改

云手机如何进行经纬度修改 云手机修改经纬度的方法因不同服务商和操作方式有所差异&#xff0c;以下是综合多个来源的常用方法及注意事项&#xff1a; 通过ADB命令注入GPS数据&#xff08;适用于技术用户&#xff09; 1.连接云手机 使用ADB工具连接云手机服务器&#xff0c;…

【微服务优化】ELK日志聚合与查询性能提升实战指南

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

transfmer学习认识

整体架构 1.自注意机制 1.1.softmax 在机器学习和深度学习中&#xff0c;softmax 函数是一个常用的激活函数&#xff0c;用于将一个向量转换为一个概率分布。softmax 函数的公式如下&#xff1a; ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/35c158988402498ba6…

在 macOS 的 ARM 架构上按住 Command (⌘) + Shift + .(点)。这将暂时显示隐藏文件和文件夹。

在 macOS 的 ARM 架构&#xff08;如 M1/M2 系列的 Mac&#xff09;上&#xff0c;设置 Finder&#xff08;访达&#xff09;来显示隐藏文件夹的步骤如下&#xff1a; 使用快捷键临时显示隐藏文件&#xff1a; 在Finder中按住 Command (⌘) Shift .&#xff08;点&#xff…

【HarmonyOS NEXT星河版开发实战】天气查询APP

目录 前言 界面效果展示 首页 添加和删除 界面构建讲解 1. 获取所需数据 2. 在编译器中准备数据 3. index页面代码讲解 3.1 导入模块&#xff1a; 3.2 定义组件&#xff1a; 3.3 定义状态变量: 3.4 定义Tabs控制器: 3.5 定义按钮样式&#xff1a; 3.6 页面显示时触发…

idea debug功能演示线程安全问题

概述 用idea debug功能演示上一篇博客中提到的 本实现中的出队、入队的实现逻辑会不会有线程安全问题&#xff1f;如果有&#xff0c;怎么解决&#xff1f; 测试用例 package com.lovehena.datastructure.test;import com.lovehena.datastructure.ArrayQueue;/* * 测试 offer…

力扣每日一题【算法学习day.132】

前言 ###我做这类文章一个重要的目的还是记录自己的学习过程&#xff0c;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&#xff01;&#xff01;&#xff01; 习题 1.统计相似字符串对的数目 题目链…

C++操作符重载案例

在学习ZLToolKit源码时&#xff0c;发现代码中涉及好多运算符重载&#xff0c;因此对其做一下归类学习。 直接写一个代码案例如下&#xff1a; #include <iostream> #include <memory> #include <functional>// 定义类 A class A { public:A(int a) { _a a…

Kafka系列之:记录一次源头数据库刷数据,造成数据丢失的原因

Kafka系列之:记录一次源头数据库刷数据,造成数据丢失的原因 一、背景二、查看topic日志信息三、结论四、解决方法一、背景 源头数据库在很短的时间内刷了大量的数据,部分数据在hdfs丢失了 理论上debezium数据采集不会丢失,就需要排查数据链路某个节点是否有数据丢失。 数据…

爬虫小案例豆瓣电影top250(json格式)

1.json格式&#xff08;仅供学习参考&#xff09; import requests, json, jsonpathclass Start(object):# 类实例化时会执行def __init__(self):self.headers {user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.…

位运算实用技巧与LeetCode实战

位操作&#xff08;Bit Manipulation&#xff09;有很多有趣的技巧&#xff0c;其中一个比较著名的资源是 Bit Twiddling Hacks 网站&#xff0c;它收集了各种位操作的高阶玩法&#xff0c;网址是&#xff1a; http://graphics.stanford.edu/~seander/bithacks.html 不过&…

Android输入事件传递流程系统源码级解析

1. 硬件层到Linux内核 设备节点&#xff1a;触摸事件由内核驱动捕获&#xff0c;写入/dev/input/eventX。关键结构体&#xff1a;input_event&#xff08;包含时间戳、类型、代码、值&#xff09;。 2. Native层处理&#xff08;system_server进程&#xff09; 2.1 EventHub …