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

【学习笔记】深入理解JVM之垃圾回收机制

【学习笔记】深入理解JVM之垃圾回收机制

更多文章首发地址:地址
请添加图片描述

参考:

  • 《深入理解JAVA虚拟机》第三版 第三章
  • 尚硅谷 第134 - 203 集
  • 参考文章:https://blog.csdn.net/qq_48435252/article/details/123697193

1、概念

🌻 首先我们要知道在程序中 垃圾 是怎么样的一个定义?

在现实中,我们知道 垃圾 是我们人类丢弃的废弃物品,而在程序中也是相对如此 垃圾 是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾。 如果不及时对内存中的垃圾进行清理,那么,这些垃圾对象所占的内存空 间会一直保留到应用程序结束,被保留的空间无法被其他对象使用。甚至可能导致内存溢出。

🌻 为什么需要垃圾回收机制呢?

面对这个问题,我们也可以想一想我们人类自己的生存问题,如果我们一直不解决 垃圾 的存储问题,则可能会导致我们生存的地方被 垃圾 所侵占,从而导致没有生存空间。

  • 对于高级语言来说,一个基本认知是,如果不进行 垃圾回收 ,内存迟早都会被消耗完,因为不断地分配内存空间而不进行回收,就好像不停地生产生活 垃圾 而从来不打扫一样。
  • 除了释放没用的对象,垃圾回收 也可以清除内存里的记录碎片。碎片整理将所占用的堆内存移到堆的一端,以便 JVM 将整理出的内存分配给新的对象。

2、常见的垃圾回收算法

概念补充:对象已死

在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就 是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能再被任何途径使用的对 象)了。

  • 判断对象存活一般有两种方式:引用计数算法可达性分析算法

我们比较常见的 垃圾回收算法 有以下几种:

  • 引用计数法
  • 可达性分析算法
  • 标记清除算法
  • 复制算法
  • 标记压缩算法

2.1 引用计数法

概念: 引用计数算法(Reference Counting) 比较简单,对每个对象保存一个整型 的引用计数器属性。用于记录对象被引用的情况。

对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加1;当引用失效时,引用计数器就减1。只要对象 A 的引用计数器的值为0,即表示对象A不可能再被使用,可进行回收。

优点:

  • 实现简单方便,回收效率高

缺点:

  • 需要单独的 字段存储计数器 ,这样的做法增加了存储空间的开销。
  • 每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销。
  • 引用计数器有一个严重的问题,即无法处理循环引用的情况。这是一 条致命缺陷,导致 在Java的垃圾回收器中没有使用这类算法(如下图)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hXfw8IC6-1674746881708)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20230126131919308.png)]

2.2 可达性分析算法

这个算法的基本思路 就是通过 一系列称为 “GC Roots” 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过 程所走过的路径称为 “引用链”(Reference Chain) ,如果某个对象到 GC Roots 间没有任何引用链相连, 或者用图论的话来说就是从 GC Roots 到这个对象不可达时,则证明此对象是不可能再被使用的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0S4nv0dN-1674746881709)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20230126133618404.png)]

Java 技术体系里面,固定可作为 GC Roots 的对象包括以下几种:

  • 栈帧中的本地变量表中引用的对象。

  • 静态变量。

  • 字符串常量池里的引用。

  • 在本地方法栈中 JNI(即通常所说的Native方法) 引用的对象。

  • Java 虚拟机内部的引用,如基本数据类型对应的 Class对象 ,一些常驻的异常对象(比如

    NullPointExcepiton、OutOfMemoryError )等,还有系统类加载器。

  • 所有被同步锁(synchronized关键字)持有的对象.

2.3 对象的finalization机制

  • Java语言提供了对象终止 (finalization) 机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。
  • 当垃圾回收器发现没有引用指向一个对象,即:垃圾回收此对象之前,总会先调用这个对象的 finalize() 方法。
  • finalize() 方法允许在子类中被重写,用于在对象被回收时进行资源释放。通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等。
  • 永远不要主动调用某个对象的 finalize () 方法,应该交给垃圾回收机制调用。理由包括下面三点:
    • ➢在 finalize() 时可能会导致对象复活。
    • finalize() 方法的执行时间是没有保障的,它完全由GC线程决定,极端情况下,若不发生GC,则 finalize() 方法将没有执行机会。
    • ➢一个糟糕的 finalize () 会严重影响GC的性能。
  • 从功能上来说,finalize()方法与C++ 中的析构函数比较相似,但是Java采用的是基于垃圾回收器的自动内存管理机制,所以finalize()方法在本质,上不同于C++ 中的析构函数。

2.4 判断对象的死亡

即使在可达性分析算法中判定为不可达的对象,也不是 “非死不可” 的,这时候它们暂时还处于 “缓刑” 阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没 有与 GC Roots 相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是 否有必要执行 finalize() 方法。假如对象没有覆盖 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过,那么虚拟机将这两种情况都视为 “没有必要执行”

如果对象重写了 finalize() 方法,且还没有执行该方法,则会把该对象插入到 F一Queue 队列中,由一个虚拟机自动创建的、低优先级的 Finalizer 线程触发其 finalize() 方法执行。

2.5 标记清除算法

概念:

如它的名字一样,算法分为 “标记”“清除” 两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。

如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Itl4yXbL-1674746881709)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20230126164851113.png)]

优点:

  • 比较简单且经常使用。

缺点:

  • 第一个是执行效率不稳定,如果 Java 堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低;
  • 第二个是内存空间的碎片化问题,标记、清除之后会产生大 量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找 到足够的连续内存而不得不提前触发另一次垃圾收集动作。

2.6 复制算法

为了解决 标记-清除算法 面对大量可回收对象时执行效率低的问题,1969年Fenichel提出了一种称为 “半区复制”(Semispace Copying) 的垃圾收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

核心思想

将活着的内存空间分为两块,每次使用一块,进行垃圾回收的时候,将存活对象复制到另一块未使用的区域,然后将源区域清空,然后交换两个内存的角色

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMUxy5lg-1674746881710)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20230126165545577.png)]

  • 优点:
    • 没有标记和清除过程,实现简单,运行高效
    • 复制过去以后保证空间连续性,不会出现 “碎片” 问题。
  • 缺点:
    • 此算法的缺点也是很明显的,就是需要两倍的内存空间。
    • 对于 G1 这种分拆成为大量 regionGC ,复制而不是移动,意味着 GC 需要维护 region 之间对象引用关系,不管是内存占用或者时间开销也不小。
    • 特别的如果系统中的可用对象很多,复制算法不会很理想,因为要复制大量的对象。

在新生代,对常规应用的垃圾回收,一次通常可以回收70%一 99%的内存空间。回收性价比很高。所以现在的商业虚拟机都是用这种收集算法回收新生代。

2.7 标记压缩算法

背景:

复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。这种情况在新生代经常发生,但是在老年代,更常见的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活对象较多,复制的成本也将很高。因此,基于老年代垃圾回收的特性,需要使用其他的算法。 标记一清除算法的确可以应用在老年代中,但是该算法不仅执行效率低下,而且在执行完内存回收后还会产生内存碎片,所以JVM的设计者需要在此基础之上进行改进。标记一压缩(Mark一Compact) 算法由此诞生。 1970年前后,G. L. Steele 、C. J. Chene和D.S. Wise 等研究者发布标记一压缩算法。在许多现代的垃圾收集器中,人们都使用了标记一压缩算法或其改进版本。

核心思想:

  • 第一阶段和标记一清除算法一样,从根节点开始标记所有被引用对象.
  • 第二阶段将所有的存活对象压缩到内存的一端,按顺序排放。
  • 之后,清理边界外所有的空间。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4sTbzzbE-1674746881710)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20230126170036703.png)]

  • 标记一压缩算法 的最终效果等同于标记一清除算法执行完成后,再进行一次内存碎片整理,因此,也可以把它称为标记一清除一压缩(Mark一 Sweep一Compact)算法。

  • 二者的本质差异在于 标记清除算法 是一种 非移动式 的回收算法,标记压缩 是移动式的。是否移动回收后的存活对象是一项优缺点并存的风险决策。

清除算法的对比:

属性\算法标记清除算法复制算法标记压缩算法
时间复杂度
空间复杂度占用2倍
内存碎片
移动对象

2.8 分代收集算法

前面所有这些算法中,并没有一种算法可以完全替代其他算法,它们都具有自己独特的优势和特点。分代收集算法应运而生。

分代收集算法,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。

一般是把 Java 堆分为 新生代老年代 ,这样就可以根据各个年代的特点使用不同的回收算法,以提高垃圾回收的效率。

目前几乎所有的GC都是采用分代收集(Generational Collecting) 算法执行垃圾回收的。

在HotSpot中,基于分代的概念,GC所使用的内存回收算法必须结合年轻代和老年代各自的特点。

  • 年轻代(Young Gen)
    • 年轻代特点:区域相对老年代较小,对象生命周期短、存活率低,回收频繁。
    • 这种情况复制算法的回收整理,速度是最快的。复制算法的效率只和当前存活对象大小有关,因此很适用于年轻代的回收。而复制算法内存利用率不高的问题,通过 hotspot 中的两个 survivor 的设计得到缓解。
  • 老年代(Tenured Gen)
    • 老年代特点:区域较大,对象生命周期长、存活率高,回收不及年轻代频繁。
    • 这种情况存在大量存活率高的对象,复制算法明显变得不合适。一般是由标记清除或者是标记整理的混合实现。
      • ➢标记阶段的开销与存活对象的数量成正比。
      • ➢清除阶段的开销与所管理区域的大小成正相关。
      • ➢压缩阶段的开销与存活对象的数据成正比。

3、内存溢出与内存泄露

3.1 内存溢出

内存溢出

  • 内存溢出 相对于 内存泄漏 来说,尽管更容易被理解,但是同样的,内存溢出 也是引发程序崩溃的罪魁祸首之一。
  • 由于 GC 一直在发展,所有一般情况下,除非应用程序占用的内存增长速度非常快,造成垃圾回收已经跟不上内存消耗的速度,否则不太容易出现 OOM 的情况。
  • 大多数情况下, GC 会进行各种年龄段的垃圾回收,实在不行了就放大招,来一次独占式的 Full GC 操作,这时候会回收大量的内存,供应用程序继续使用。
  • javadoc 中对 OutOfMemoryError 的解释是,没有空闲内存,并且垃圾收集器也无法提供更多内存。

首先说没有空闲内存的情况:说明Java虚拟机的堆内存不够,原因有二:

  • (1) Java 虚拟机的堆内存设置不够。 比如:可能存在 内存泄漏 问题;也很有可能就是堆的大小不合理,比如我们要处理比较可观的数据量,但是没有显式指定 JVM 堆大小或者指定数值偏小。我们可以通过参数 一Xms、一Xmx 来调整。
  • 2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)对于老版本的 Oracle JDK ,因为永久代的大小是有限的,并且 JVM 对永久代垃圾回收(如,常量池回收、卸载不再需要的类型)非常不积极,所以当我们不断添加新类型的时候,永久代出现 OutOfMemoryError 也非常多见,尤其是在运行时存在大量动态类型生成的场合;类似 intern 字符串缓存占用太多空间,也会导致 OOM 问题。对应的异常信息,会标记出来和永久代相关: “java. lang. OutOfMemoryError: PermGen space” 。 随着元数据区的引入,方法区内存已经不再那么窘迫,所以相应的OOM有所改观,出现OOM,异常信息则变成了:“java. lang. OutOfMemoryError: Metaspace" 。 直接内存不足,也会导致 OOM

这里面隐含着一层意思是,在抛出 OutOfMemoryError 之 前,通常垃圾收集器会被触发,尽其所能去清理出空间。

  • ➢例如:在引用机制分析中,涉及到JVM会去尝试回收软引用指向的对象等。
  • ➢在 java.nio.BIts.reserveMemory() 方法中,我们能清楚的看到,System.gc() 会被调用,以清理空间。
    当然,也不是在任何情况下垃圾收集器都会被触发的。
  • 比如,我们去分配一一个超大对象,类似一个超大数组超过堆的最大值, JVM 可以判断出垃圾收集并不能解决这个问题,所以直接拋出 OutOfMemoryError

3.2 内存泄漏(Memory Leak)

  • 也称作 “存储渗漏” 。严格来说,只有对象不会再被程序用到了,但是 GC 又不能回收他们的情况,才叫 内存泄漏
  • 但实际情况很多时候一些不太好的实践(或疏忽)会导致对象的生命周期变得很长甚至导致内存溢出 OOM ,也可以叫做宽泛意义上的 内存泄漏 .
  • 尽管内存泄漏并不会立刻引起程序崩溃,但是一旦发生内存泄漏,程序中的可用内存就会被逐步蚕食,直至耗尽所有内存,最终出现0utOfMemory 异常,导致程序崩溃。

注意,这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。

举例

  • 1、单例模式 单例的生命周期和应用程序是一样长的,所以单例程序中,如果持有对外部对象的引用的话,那么这个外部对象是不能被回收的,则会导致内存泄漏的产生。
  • 2、一些提供 close 的资源未关闭导致内存泄漏 数据库连接( dataSourse. getConnection()),网络连接(socket)和io连接必须手动close,否则是不能被回收的。

4、垃圾回收器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2InjTRGb-1674746881710)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20230126200435618.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QmR3vxFn-1674746881710)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20230126200509562.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J2i6T7P2-1674746881710)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20230126200523570.png)]

  • 1.优先调整堆的大小让JVM自适应完成。
  • 2.如果内存小于100M,使用串行收集器
  • 3.如果是单核、单机程序,并且没有停顿时间的要求,串行收集器
  • 4.如果是多CPU、需要高吞吐量、允许停顿时间超过1秒,选择并行或者JVM自己选择
  • 5.如果是多CPU、追求低停顿时间,需快速响应(比如延迟不能超过1秒,如互联网应用),使用并发收集器
  • 官方推荐G1,性能高。现在互联网的项目,基本都是使用G1。

相关文章:

【学习笔记】深入理解JVM之垃圾回收机制

【学习笔记】深入理解JVM之垃圾回收机制 更多文章首发地址:地址 参考: 《深入理解JAVA虚拟机》第三版 第三章尚硅谷 第134 - 203 集参考文章:https://blog.csdn.net/qq_48435252/article/details/123697193 1、概念 🌻 首先我们…...

49.在ROS中实现local planner(2)- 实现Purepersuit(纯跟踪)算法

48.在ROS中实现local planner(1)- 实现一个可以用的模板实现了一个模板,接下来我们将实现一个简单的纯跟踪控制,也就是沿着固定的路径运动,全局规划已经规划出路径点,基于该路径输出相应的控制速度 1. Pur…...

Allegro如何设通孔Pin和Via的消盘操作指导

Allegro如何设通孔Pin和Via的消盘操作指导 用Allegro做PCB设计的时候,除了可以在光绘设置里面设置内层通孔Pin和Via的消盘,在设计过程中,同样也可以设置消盘效果,以便实时显示,如下图 如何设置,具体操作如下 点击Setup点击Unused Pads Suppression...

Android工厂模式

工厂模式分为三种 :简单工厂模式 、工厂方法模式 、抽象工厂模式 。 目录 简单工厂模式 UML图 实现 使用场景: 优点 : 缺点: 工厂方法模式 UML图 实现 使用场景: 优点: 缺点: 抽象工厂模式 UM…...

神经网络硬件加速器-架构篇

架构设计 常规架构通常包括两种: 1、全流水线架构,顾名思义,将整个神经网络进行平铺,并对每一层进行优化设计,优点:实现高吞吐率和低延时。缺点:消耗大量硬件资源,通常无法跨网络或…...

Python raise用法(超级详细,看了无师自通)

是否可以在程序的指定位置手动抛出一个异常?答案是肯定的,Python 允许我们在程序中手动设置异常,使用 raise 语句即可。 大家可能会感到疑惑,即我们从来都是想方设法地让程序正常运行,为什么还要手动设置异常呢&#…...

1.SpringSecurity快速入门

*SpringScurity的核心功能: 认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户 授权:经过认证后判断当前用户是否有权限进行某个操作 *第一步:创建springboot工程 *第二步:引入SpringSecurity依赖 *第三步:写controller,访问对应的url:localhos…...

Graph Partition: Edge cut and Vertex cut

Graph PartitionEdge cut and Vertex cutEdge cutVertex cut实际如何进行点分割和边分割的呢?Graph store format情况1:按照边列表存储:情况2:按照邻接表存储:Edge cut and Vertex cut 图结构描述了数据流动&#xff…...

Javascript周学习小结(初识,变量,数据类型)

JS的三大书写方式行内式如图所示:几点说明:JS的行内式写在HTML的标签内部,(常以on开头),如onclick行内式常常使用单引号括住字符串以区分HTML的双引号可读性差,不建议使用引号易出错,不建议使用特殊情况下使…...

C语言-基础了解-10-C函数

C函数 一、C函数 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上&…...

【LeetCode】剑指 Offer(16)

目录 题目:剑指 Offer 33. 二叉搜索树的后序遍历序列 - 力扣(Leetcode) 题目的接口: 解题思路: 代码: 过啦!!! 写在最后: 题目:剑指 Offer …...

第三十九章 linux-并发解决方法二(互斥锁mutex)

第三十九章 linux-并发解决方法二(互斥锁mutex) 文章目录第三十九章 linux-并发解决方法二(互斥锁mutex)互斥锁的定义与初始化互斥锁的DOWN操作互斥锁的UP操作用count1的信号量实现的互斥方法还不是Linux下经典的用法,…...

脚本方式本地仓库jar包批量导入maven私服

脚本内容,将以下内容保存为mavenimport.sh,放置于需要上传的目录下,可以是顶层目录,或者某个分包的目录,若私服已有待上传的包,则执行会被替换 #!/bin/bash # copy and run this script to the root of th…...

【c++】引用的学习

引用的定义和声明 引用是一种别名,它允许使用与原变量相同的内存位置。在C中,引用是使用&符号来定义的。引用必须在定义时初始化,并且可以与原变量分别使用。 int a 10; int& b a; // 定义了一个引用b,它指向a引用的作用…...

linux 软件安装及卸载

1.联网在线安装及卸载ubuntu环境下:使用apt-get 工具apt-get install - 安装软件包apt-get remove - 移除(卸载)软件包CentOS环境下:使用yum工具 (银河麒麟系统属于centos)yum install - 安装软件包yum rem…...

XShell连接ubuntu20.04.LTS

1 下载XshellXShell官方下载地址打开XSHELL官方下载地址,我们可以选择【家庭和学校用户的免费许可证】,输入邮箱之后即可获得下载链接安装非常简单,跟着提示进行即可。2 连接ubuntu2.1 查看ubuntu的ip地址输入命令查看ip地址ifconfig刚开始可…...

【FPGA】Verilog:MSI/LSI 组合电路之解码器 | 多路分解器

写在前面:本章将理解编码器与解码器、多路复用器与多路分解器的概念,通过使用 Verilog 实现多样的解码器与多路分解器,通过 FPGA 并使用 Verilog 实现。 Ⅰ. 前置知识 0x00 解码器与编码器(Decoder / Encoder) 解码器…...

深入理解JDK动态代理原理,使用javassist动手写一个动态代理框架

文章目录一、动手实现一个动态代理框架1、初识javassist2、使用javassist实现一个动态代理框架二、JDK动态代理1、编码实现2、基本原理(1)getProxyClass0方法(2)总结写在后面一、动手实现一个动态代理框架 1、初识javassist Jav…...

一、策略模式的使用

1、策略模式定义: 策略模式(Strategy Pattern)定义了一组策略,分别在不同类中封装起来,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。比如我们要去某个地方,会根据距…...

Verilog使用always块实现时序逻辑

这篇文章将讨论 verilog 中一个重要的结构---- always 块(always block)。verilog 中可以实现的数字电路主要分为两类----组合逻辑电路和时序逻辑电路。与组合逻辑电路相反,时序电路电路使用时钟并一定需要触发器等存储元件。因此&#xff0c…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

HTML 列表、表格、表单

1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...

rknn toolkit2搭建和推理

安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...