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

JVM ②-双亲委派模型 || 垃圾回收GC

这里是Themberfue 

  •  在上节课对内存区域划分以及类加载的过程有了简单的了解后,我们再了解其他两个较为重要的机制,这些都是面试中常考的知识点,有必要的话建议背出来,当然不是死记硬背,而是要有理解的背~~~
  • 如果对 JVM 的其他机制感兴趣的话,建议去阅读一些书籍,我的建议是失眠的时候看看,可以有效改善睡眠(bushi

类加载器 

  • 类加载器从 JDK 1.0 就出现了,最初只是为了满足 Java Applet(已经被淘汰) 的需要。后来,慢慢成为 Java 程序中的一个重要组成部分,赋予了 Java 类可以被动态加载到 JVM 中并执行的能力。
  • 我们先看官方 API 文档的介绍:A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class. A typical strategy is to transform the name into a file name and then read a "class file" of that name from a file system. Every Class object contains a reference to the ClassLoader that defined it. Class objects for array classes are not created by class loaders, but are created automatically as required by the Java runtime. The class loader for an array class, as returned by Class.getClassLoader() is the same as the class loader for its element type; if the element type is a primitive type, then the array class has no class loader.
  • 简单来说:
  • 在 Java 中,类加载器(ClassLoader)是负责加载类文件(.class 文件)的组件。类加载器的主要作用是将字节码加载到 JVM 中,并使其成为JVM 可识别并执行的类
  • 类加载器是Java 运行时环境的一部分它控制了类的加载过程,并且是 JVM 的重要组成部分之一。类加载器是一个负责加载类的对象,用于实现类加载过程中的加载这一步。
  • 每个类都是通过 类加载器加载的。类加载器将字节码文件加载到内存,并将其转化为 JVM 内部的 Class 对象,从而使得程序能够使用该类。
  • 每个 Java 类都有一个引用指向加载它的 ClassLoader
  • 数组类不是通过 ClassLoader 创建的(数组类没有对应的二进制字节流),是由 JVM 直接生成的。
    class Class<T> {...private final ClassLoader classLoader;@CallerSensitivepublic ClassLoader getClassLoader() {//...}...
    }

  • 在前边多线程的学习中,我们学习了懒汉模式,Java 类加载的规则也是采用懒汉模式来加载类的。
  • JVM 启动时,并不会一次性加载所有类,而是根据需要动态加载类。也就是说,大部分类在具体用到的时候才会去加载,这样对内存更加友好。这便是懒汉模式一个应用~~~
  • 对于已经加载的类会被放在 ClassLoader 中。在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。也就是说,对于一个类加载器来说,相同二进制名称的类只会被加载一次。
    public abstract class ClassLoader {...private final ClassLoader parent;// 由这个类加载器加载的类。private final Vector<Class<?>> classes = new Vector<>();// 由VM调用,用此类加载器记录每个已加载类。void addClass(Class<?> c) {classes.addElement(c);}...
    }

分类

Bootstrap ClassLoader(引导类加载器)

  • 作用:负责加载 JDK 核心类库,也就是 Java标准库(如 java.lang.*java.util.* 等)。
  • 加载路径:它从 JRE 的 lib 目录下加载类,如 rt.jarresources.jar 等。
  • 特点:Bootstrap 类加载器是 JVM 自带的,用 C++ 实现,不能被 Java 代码继承。它加载的类在 JVM 启动时就被加载到内存。

Extension ClassLoader(扩展类加载器)

  • 作用:负责加载 JDK 扩展类库,即 JRE/lib/ext/ 目录下的类,或者 java.ext.dirs 指定的路径中的类。
  • 加载路径:默认加载路径是 JRE/lib/ext/ 目录下的 JAR 文件和类文件。
  • 特点Extension ClassLoader 是由 ClassLoader 继承而来,用 Java 实现。

Application ClassLoader(应用类加载器)

  • 作用:负责加载 应用程序的类库,即 classpath 下的类。
  • 加载路径:从 classpath 环境变量指定的路径加载类文件(例如:.class 文件或 .jar 包)。
  • 特点:是最常见的类加载器,它负责加载我们自己编写的 Java 类文件,通常由 java.lang.ClassLoader 类提供。Java 的第三方库,例如使用 Maven 管理的库

自定义类加载器(Custom ClassLoader)

  • 作用:开发者可以继承 ClassLoader 类,重新实现 findClass() 方法,来创建自己特定的类加载器。
  • 用途:自定义类加载器通常用于 插件化、动态加载、热部署 等场景。

类加载器的父子关系

  • Java 中的类加载器是按照父子层级结构组织的。每个类加载器都有一个父类加载器,类加载的过程遵循 父加载器优先加载的原则。
  • 类加载器的父子关系如下:
    • Bootstrap ClassLoader 是根加载器,它不继承任何类加载器。
    • Extension ClassLoaderBootstrap ClassLoader 的子类。
    • Application ClassLoaderExtension ClassLoader 的子类。

双亲委派模型

  • Java 的类加载机制采用了 双亲委派模型。该模型的核心思想是:每个类加载器都会首先委托给父类加载器进行加载,只有父类加载器无法加载时,子类加载器才会尝试加载。
  • 流程:当一个类加载器需要加载一个类时,它会 首先委托给父加载器。如果父加载器不能加载该类(即类不存在),子加载器才会 自己尝试加载该类。如果子加载器依然不能加载该类,则 返回错误
  • 简单来说就是:在进行类加载时,也就是通过全限定类名找 .class 文件时,会先从 Application ClassLoader 作为入口开始,随后把 加载类 这样的任务委托给其父类,也就是 Extension ClassLoader 来进行。Extension ClassLoader 也不会立刻开始 加载类 的任务,而是继续委托给其父类,也就是 Bootstrap ClassLoader 来进行。Bootstrap Loader 作为顶层类加载器,自然没有父类了,便会从 Bootstrap Loader 这一层开始查找,也就是先从 Java标准库 开始查找。如果 Bootstrap Loader 没有找到,则交给其子类 Extension ClassLoader 查找,若 Extension ClassLoader 没有找到,则交给其子类 Application ClassLoader 查找,若依旧没有找到且没有子类时,则抛出异常

  • ⚠️双亲委派模型并不是一种强制性的约束,只是 JDK 官方推荐的一种方式。
  • 其实这个双亲翻译的容易让别人误解,我们一般理解的双亲都是父母,这里的双亲更多地表达的是“父母这一辈”的人而已,并不是说真的有一个 MotherClassLoader 和一个FatherClassLoader 。个人觉得翻译成单亲委派模型更好一些,不过,国内既然翻译成了双亲委派模型并流传了,按照这个来也没问题,不要被误解了就好。 
  • 双亲委派模型保证了 Java 程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类),也保证了 Java 的核心 API(如 java.lang.String)不被篡改。

类加载器的应用

自定义类加载器

  • 在一些特殊的应用场景下,如 热部署、插件机制、动态代理 等,可以通过 自定义类加载器 来控制类的加载方式。例如,JSP 热部署、Spring 容器的 Bean 加载等,都需要自定义类加载器。

Java 的反射机制

  • 反射机制(如 Class.forName())通过类加载器动态加载类,并获取类的构造方法、字段、方法等信息,支持 运行时动态创建对象调用方法 等操作。

隔离加载

  • Java 类加载器的父子层次结构使得不同应用程序可以 隔离类的加载,避免不同程序之间的类冲突。例如,Tomcat、Jetty 等应用服务器支持 Web 应用程序的隔离,它们为每个应用创建不同的类加载器,防止不同应用之间共享类。

 垃圾回收(GC)

  • 在过去 C语言时,我们在申请了一块内存空间后,要去手动释放这块内存,也就是调用 free() 函数,若不释放掉,很容易造成内存泄漏和内存溢出等问题。就算代码块里确实调用了 free() 函数,有时候程序也可能因为 bug 没有走到那里,从而导致内存泄漏等。
  • 手动释放内存太麻烦了,所以 Java 为了开发的便捷性,便引入 GC(Garbage Collection,垃圾回收) 机制,JVM 会自动识别出哪块内存是不需要使用的,并释放掉这些内存,不再需要我们过度关注。
  • 相比较于 C++,Java 的代码执行时间要短一些,就是因为 JVM 的这些机制。不过,随着 JDK 版本的不断迭代,目前 Java 的执行时间以及和 C++ 较为接近了,STW 时间大部分情况下都在 1ms 以下。(STW,Stop The World,在 JVM GC 时,就会因为需要GC,其他业务的代码不得不停止运行,必须等待 GC 执行完后才能继续运行,这个时间间隔就被称为 STW

  • Java 的 GC 主要是针对对象内存的回收和对象内存的分配。所以,Java GC 最核心的功能是  内存中对象的分配与回收。 是 GC 主要管理的区域,所以 堆 也被称作 GC 堆(Garbage Collected Heap)。在 Java 代码执行的过程中,必然伴随着新对象的创建旧对象的消亡,所以,回收内存 的本质就是 回收对象
  • 在 Java 中,对象的生命周期通常由 JVM 管理,当对象不再被引用时,它就变成了 "垃圾",这些对象占用了内存资源。没有垃圾回收机制,程序员需要手动清理这些不再使用的对象,这可能导致:内存泄漏:程序员没有正确地释放不再使用的对象,导致内存不能被回收。内存溢出:程序分配的内存资源不能及时释放,导致系统内存不足,程序崩溃。

GC 的工作过程大致可以分为两步:

  1. 找到 垃圾,即为不再使用的对象(死亡对象)
  2. 释放 垃圾,该对象所申请的内存释放掉

死亡对象的判断

引用计数法

在 new 对象时,给对象单独添加一个引用计数器:

  • 每当有一个地方引用它,计数器就加 1;
  • 当引用失效(置为 null ),计数器就减 1;
  • 任何时候计数器为 0 的对象就是不可能再被使用的(即为死亡对象)。

这便是 PythonPHP 使用的方法,这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,是因为其存在某些致命的缺点。

  • 内存消耗:需要为每个对象都额外开辟一块空间添加计数器,如果一个对象的大小为8字节,计数器的空间通常为4字节,这就比原来多了 50% 的内存空间。
  • 对象之间循环引用:所谓对象之间的相互引用问题,如下面代码所示:除了对象 objAobjB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们。
    public class Test {Object instance = null;public static void main(String[] args) {Test objA = new Test();Test objB = new Test();objA.instance = objB;objB.instance = objA;objA = null;objB = null;}
    }

可达性分析

引用计数法在空间上的开销较大,而可达性分析则是用时间换空间的思想。

  • 以代码中一些特定的对象,作为 GCRoots,也就是遍历的起点,从 GCRoots 开始向下搜索,节点所走过的路径称为引用链,所经过的对象被标记为 “可达”,当一个对象到 GC Roots 没有任何引用链相连的话,该对象则被标记为 “不可达”,则证明此对象是不可用的,需要被回收。
  • 下图为一些对象的引用关系,其中,Obj5 - Obj7 之间虽然有引用关系,但是从 GCRoots开始并不能遍历到 Obj5 - Obj7,被认为不可达,然后回收。
  • 可达性分析 会尽可能的遍历每一个对象,遍历完之后将那些 “不可达” 的对象进行释放。
  • 但是,即使在可达性分析法中不可达的对象,也并非是 “非死不可” 的,这时候它们暂时处于 “缓刑阶段”,要真正宣告一个对象的死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。
  • Object 类中的 finalize 方法一直被认为是一个糟糕的设计,成为了 Java 语言的负担,影响了 Java 语言的安全和 GC 的性能。JDK9 版本及后续版本中各个类中的 finalize 方法会被逐渐弃用移除。忘掉它的存在吧~~~

❓哪些对象可以作为 GCRoots

  • 虚拟机栈(栈帧中的局部变量表)中引用的对象
  • 本地方法栈(Native 方法)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 所有被同步锁持有的对象
  • JNI(Java Native Interface)引用的对象

可达性分析便是 Java 采取的方案,这种方案的弊端也很明显,在每次可达性分析时,都会尽可能的遍历所有对象,如果对象过多,其所消耗的时间也会相应的增长,STW 的时间就会变大。


垃圾收集算法

标记-清除算法

找到可以清除的垃圾对象后,我们还需要对其进行释放。标记-清除算法Mark and Sweep)是最基础的垃圾回收算法。后续的算法都是根据这个算法的缺点进行优化得来的。

  • 它分为两个阶段:
  • 标记阶段:遍历所有对象,标记出哪些对象是可以访问的(即有引用指向的对象)。
  • 清除阶段:回收所有没有标记的对象,释放它们占用的内存。

这个算法的缺点也十分明显。首先体现在效率上,标记和清除这两个过程的效率都不高。再者,从空间上看,标记清除后会产生大量不连续的内存碎片

  • 我们申请内存时,要求这块内存必须是连续的,不能是分散的。
  • 假设你需要申请 1GB 的内存空间,目前空闲内存空间为 4GB,但依然申请失败,这是因为 4GB 的内存大部分都是零零散散分布的,并非连续的。

复制算法

复制算法(Copying)将内存分为两部分:一部分用来存储活动对象,另一部分为空闲区。每次垃圾回收时,将存活的对象复制到另一部分内存上,从而回收掉已经不再使用的内存。

  • 该算法虽然解决了标记-清除算法带来的内存碎片问题。
  • 但需要额外的内存空间,通常是原内存空间的两倍大小,这对于内存的空间利用率是非常低的。
  • 如果存活的对象过多,复制所消耗的时间和资源也是巨大的。

标记-整理算法

标记-整理算法(Mark-Compact)是标记-清除算法的改进版,标记阶段仍然和标记-清除算法相同,但清除阶段不再是直接删除不可达对象,而是将存活对象整理到内存的一端,最后释放出内存的连续空间。

  • 虽然解决了内存碎片的问题以及空间利用率低的问题。
  • 但是相较于标记-清除算法,标记-整理算法的实现更加复杂。
  • 且由于多了整理这一步,因此效率也不高,适合这种垃圾回收频率不是很高的场景

分代收集算法

分代收集算法(Generational Garbage Collection)基于这样一个假设:大部分对象都很快会变成垃圾。因此将堆内存划分为多个区域(如:新生代(伊甸区、幸存区)、老年代),通过将新生代的对象频繁回收来提高回收效率。

  • 堆内存通常被分为以下三个部分:
  1. 新生代内存(Young Generation)
  2. 老生代(Old Generation)
  3. 永久代(Permanent Generation)
  • 下图所示的 Eden(伊甸) 区、两个 Survivor(幸存) 区 S0S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代。JDK 8 版本之后 PermGen(永久) 已被 Metaspace(元空间) 取代,元空间使用的是直接内存,用于存储类元信息、静态变量等。 

分代垃圾回收集具体过程

  • 分代收集算法根据不同 “代” 次区分对象的大小,每进行 GC 一次,每个对象的 “代” 次就会 +1。大多数对象最开始都是 0 代,也就是放在 Eden 区。⚠️每次 Minor GC 后,代数才会 +1。

新生代垃圾回收(Minor GC)

  • 当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC(这是一个特殊的 GC)
  • Minor GC 只扫描新生代(而不会扫描整个堆)。
  • 由于大部分对象都存活不到 Minor GC 发生,所以此时所剩的对象也不多,此时 Minor GC 会回收 Eden 中的垃圾对象,存活对象会被复制到 Survivor 区(S0 或 S1)。因为存活对象不多,只需要付出少量对象的复制成本就可以完成每次垃圾收集,所以使用复制算法并不会产生过大的开销。
  • 如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间(s0 或者 s1)中,并将对象代设为 1(Eden 区 -> Survivor 区后对象的初始代数变为 1)。
  • 对于 Survivor 区的对象也会进行 GC 回收,伴随着每次 Minor GC,Survivor 区的对象的 “代” 次也会相应的增加,Survivor 区的对象会在 S0 和 S1 区反复横跳多次 GC 仍存活的对象,达到一定阈值(默认为 15 代)后,晋升到 老年代。⚠️如果 Survivor 区满,存活对象直接晋升到 老年代

老年代垃圾回收(Major GC / Full GC)

  • 当老年代内存不足时(如 Survivor 区晋升过来的对象太多)、显式调用 System.gc() 时、Minor GC 失败(即新生代对象无法晋升到老年代,而老年代已满)时会触发 Major GC
  • 由于老年代的对象可能较少且生命周期都很长,所以使用标记-清除算法标记存活对象并清除不可达对象,且使用标记-整理算法整理存活对象,避免内存碎片。

Full GC

  • Full GC 代表的是 全堆垃圾回收,即 同时回收新生代和老年代
  • 老年代满了(如 Minor GC 失败后,需要腾出老年代空间)、元空间(Metaspace)满了调用 System.gc() 时会触发 Full GC
  • 由于 Full GC 影响较大,因为它会暂停所有线程,通常需要尽量避免。

  • Survivor 区溢出:如果 Survivor 区装不下存活对象,则直接晋升老年代。
  • 大对象直接进入老年代:大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。会直接分配到老年代,避免在新生代频繁移动。
  • 大对象直接进入老年代的行为是由虚拟机动态决定的,它与具体使用的垃圾回收器和相关参数有关。大对象直接进入老年代是一种优化策略,旨在避免将大对象放入新生代,从而减少新生代的垃圾回收频率和成本。

JVM的垃圾回收器 

Java 提供了多种垃圾回收器,常见的有:

Serial GC

  • 特点:单线程工作,所有的垃圾回收工作都由一个线程完成。
  • 适用场景:适合内存较小、单核 CPU 的环境。

Parallel GC(并行回收)

  • 特点:多个线程并行进行垃圾回收,适合多核 CPU 的环境。
  • 适用场景:适合 CPU 核心数多的环境,能显著提高 GC 的吞吐量。

CMS(Concurrent Mark-Sweep)

  • 特点:旨在最小化垃圾回收停顿时间。它通过并行标记阶段和并发清除阶段来减少 GC 停顿时间。
  • 适用场景:适合对响应时间有严格要求的应用,如 Web 应用。

G1 GC(Garbage-First GC)

  • 特点:G1 是一种新型的垃圾回收器,它通过将堆划分为多个区域进行回收,以最小化停顿时间,并保持较高的吞吐量。G1 GC 可以动态调节回收的停顿时间,具有较好的可预测性。
  • 适用场景:适用于大内存和对延迟有较高要求的系统。

  • JVM 机制的介绍到此就结束了,之后更新什么内容之后再说吧~~~
  • 毕竟不知后事如何,且听下回分解 
  • ❤️❤️❤️❤️❤️❤️❤️
  • ⚠️⚠️⚠️本文章部分文案参考——JavaGuide

相关文章:

JVM ②-双亲委派模型 || 垃圾回收GC

这里是Themberfue 在上节课对内存区域划分以及类加载的过程有了简单的了解后&#xff0c;我们再了解其他两个较为重要的机制&#xff0c;这些都是面试中常考的知识点&#xff0c;有必要的话建议背出来&#xff0c;当然不是死记硬背&#xff0c;而是要有理解的背~~~如果对 JVM …...

内容中台驱动企业数字化内容管理高效协同架构

内容概要 在数字化转型加速的背景下&#xff0c;企业对内容管理的需求从单一存储向全链路协同演进。内容中台作为核心支撑架构&#xff0c;通过统一的内容资源池与智能化管理工具&#xff0c;重塑了内容生产、存储、分发及迭代的流程。其核心价值在于打破部门壁垒&#xff0c;…...

人工智障的软件开发-自动流水线CI/CD篇-docker+jenkins部署之道

指令接收&#xff1a;「需要自动构建系统」 系统检测&#xff1a;目标开发一个软件已完成代码仓库-轻盈的gitea&#xff0c;开始添加自动流水线 启动应急冷却协议&#xff1a;准备承受Java系应用的资源冲击 核心组件锁定&#xff1a;构建老将军Jenkins&#xff08;虽然年迈但依…...

数字人技术之LatentSync Win11本地部署

#LatentSync技术原理 字节跳动开源的基于音频条件潜在扩散模型的端到端唇同步框架,基于潜在扩散模型,以音频条件潜在扩散模型为基础&#xff0c;利用 Stable Diffusion 强大能力&#xff0c;直接建模复杂的音频与视觉之间的关系&#xff0c;实现高质量的唇形同步. 从而制作虚拟…...

Llama3.0论文学习笔记: The Llama 3 Herd of Models

1. 写在前面 今天分享Llama3.0的论文&#xff0c;2024.7月来自Meta的Llama团队&#xff0c;2025年1月DeepSeek R1出现之后&#xff0c;其风头显然已经盖住了Llama3&#xff0c;这时候整理Llama3感觉有点赶不上潮流了&#xff0c;但是我还是想整理下Llama3.0&#xff0c;原因是…...

C#学习之数据转换

目录 一、创作说明 二、数据类型之间的转换 1.数据类型之间的转换表格 2.代码示例 三、进制之间的转换 1.进制之间的转换表格 2.代码示例 四、ASCII 编码和字符之间的转换 1.ASCII 编码和字符之间的转换表格 2.代码示例 五、总结 一、创作说明 C#大多数时候都是和各…...

POI 和 EasyExcel

前言 将表格信息导出为Excel表格&#xff08;导出数&#xff09;将Excel表格信息录入到数据库&#xff08;导入数据&#xff09; 操作Excel目前比较流行的就是 Apache POI 和阿里巴巴的 EasyExcel Apache POI Apache POI 官网&#xff1a;https://poi.apache.org/ HSSF&am…...

分布式光纤传感:为生活编织“感知密网”

分布式光纤测温技术虽以工业场景为核心&#xff0c;但其衍生的安全效益已逐步渗透至日常生活。 分布式光纤测温技术&#xff08;DTS&#xff09;作为一种先进的线型温度监测手段&#xff0c;近年来在多个领域展现了其独特的优势。虽然其核心应用场景主要集中在工业、能源和基础…...

Web后端 - Maven管理工具

一 Maven简单介绍 Maven是apache旗下的一个开源项目&#xff0c;是一款用于管理和构建java项目的工具。 Maven的作用 二 Maven 安装配置 依赖配置 依赖传递 依赖范围 生命周期 注意事项&#xff1a;在同一套生命周期中&#xff0c;当运行后面的阶段时&#xff0c;前面的阶段都…...

聊一聊vue如何实现角色权限的控制的

大家好&#xff0c;我是G探险者。 关于角色与权限控制&#xff0c;通常是分为两大类&#xff1a;一种是菜单权限&#xff1b;一种是操作权限。 菜单权限是指&#xff0c;每个角色对应着可以看到哪些菜单&#xff0c;至于每个菜单里面的每个按钮&#xff0c;比如增删改查等等这类…...

Java中使用EasyExcel

Java中使用EasyExcel 文章目录 Java中使用EasyExcel一&#xff1a;EasyExcel介绍1.1、核心函数导入数据导出数据 1.2、项目实际应用导入数据导出数据 1.3、相关注解ExcelProperty作用示例 二&#xff1a;EasyExcel使用2.1、导入功能2.2、导出功能 三&#xff1a;EasyExcel完整代…...

LLM:GPT 系列

阅读原文&#xff1a; LLM&#xff1a;Qwen 系列 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是生成式预训练语言模型&#xff0c;基于 Transformer 架构&#xff0c;专注于通过自回归的方式生成自然语言文本&#xff0c;即给定一个输入序列 x { x 1 , …...

【已解决】TypeError: AsyncConnectionPool.__init__(), new install bug, httpx==0.24.1

1&#xff0c;参考社区链接&#xff0c;首先降低gradio版本&#xff0c;降低到4以下&#xff0c;但是也不能降太低&#xff0c;也不能太高&#xff0c;要适中&#xff0c;推荐版本3.39.0 pip install gradio3.39.0 2&#xff0c;下载正确的httpx版本 参考社区链接&#xff0…...

Linux:深入了解进程信号(上)

目录 1. 什么是信号 1.1 引入 1.2 概念 1.3 特性 1.4 信号的三个方面 2. 信号的产生 2.1 键盘按键产生 2.2 signal捕捉信号函数 2.3 发送信号原理 2.4 硬件中断 2.5 指令和函数接口 2.5.1 kill指令 2.5.2 kill函数 2.5.3 raise与abort函数 2.6 软件条件 2.7 异…...

Java小白入门基础知识(二)

1.标识符 概念: 在程序中给类,方法,变量取的名称叫做标识符 规范: 字母,数字,下划线,$符号 注意: 不能以数字开头,也不能是关键字,严格区分大小写(一般定义常量就是大写) 软性建议: 1)类名:每个单词首字母大写(大驼峰) 2)方法名:首字母小写,后面每个单词首字母大写(小驼…...

Servlet中,WebServlet注解的使用方法

案例&#xff1a;声明abc接口&#xff0c;在接口内部获取配置信息 WebServlet(urlPatterns"/abc",loadOnStartup6,initParams {WebInitParam(name"username", value"mmm", description"this is username"),WebInitParam(name"a…...

重新出发的LLM本地部署——DeepSeek加持下的Ollama+OpenWebUI快速部署

DeepSeek 这真的是太惊艳了&#xff0c;发布出来的模型这么能打&#xff0c;在线的版本使用起来也是丝滑连招&#xff0c;感觉效果比起之前一直用智谱 chatglm4 更好用&#xff0c;想着本地化部署一下。 本来以为&#xff0c;会和之前在开发测试 transformers 的模型和代码一样…...

【Python爬虫(1)】专栏开篇:夯实Python基础

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…...

低代码组态软件-BY组态

引言 在工业4.0与智能制造浪潮的推动下&#xff0c;组态软件作为工业自动化的核心工具&#xff0c;正逐步从传统单机模式向Web化、智能化方向演进。BY组态作为一款基于Web的嵌入式组态插件工具&#xff0c;凭借其低代码、高灵活性和跨平台特性&#xff0c;成为工业物联网&#…...

【计算机网络】数据链路层数据帧(Frame)格式

在计算机网络中&#xff0c;数据帧&#xff08;Frame&#xff09; 是数据链路层的协议数据单元&#xff08;PDU&#xff09;&#xff0c;用于在物理介质上传输数据。数据帧的格式取决于具体的链路层协议&#xff08;如以太网、PPP、HDLC 等&#xff09;。以下是常见数据帧格式的…...

[特殊字符] C语言中打开和关闭文件的两种方法:标准库 VS 系统调用

C语言中对文件打开关闭操作 前言方法一&#xff1a;标准输入输出库&#xff08;stdio.h&#xff09;—— 高级文件操作的利器打开文件&#x1f4a1; 关闭文件&#xff1a;fclose示例代码&#x1f4dd; 个人见解 方法一&#xff1a;系统调用&#xff08;fcntl.h 和 unistd.h&…...

如何将ubuntu下的一个目录,保存目录结构为一个git仓库并上传

目录 1. 初始化本地Git仓库 2. 添加文件到仓库 3. 提交更改 4. 创建并关联远程仓库 5. 推送代码到远程仓库 完整流程总结 要将Ubuntu下的一个目录&#xff08;例如rpc&#xff09;保存为一个Git仓库并上传到远程仓库&#xff0c;您可以遵循以下步骤&#xff1a; 1. 初始…...

应用分层、三层架构和MVC架构

前言 在前面中&#xff0c;我们已经学习了Spring MVC 的一些基础操作&#xff0c;那么后面就用一些简单的案例来巩固一下。 在开始学习做案例之前&#xff0c;我们先来了解一下在软件开发中常见的设计模式和架构。 应用分层 含义 应用分层是一种软件开发设计思想&#xff0…...

前端实现防抖功能的详细解读

在前端开发中&#xff0c;防抖&#xff08;Debounce&#xff09; 是一种优化技术&#xff0c;用于限制某个函数在短时间内被频繁调用的次数。它的核心思想是&#xff1a;在一定时间内&#xff0c;无论触发多少次事件&#xff0c;只执行最后一次操作。防抖通常用于处理用户输入、…...

VUE3环境搭建

最近准备用Vue编写一点前端页面&#xff0c;我在前端一直是个小白&#xff0c;之前用的Vue2写了几个页面&#xff0c;现在已经是VUE3了&#xff0c;重新安装下环境开始。 1.npm安装 Vue需要用npm安装&#xff0c;npm是nodejs的package manager&#xff0c;这里我们安装下node…...

1-16 tortoiseGit分支与Git操作

1-1 创建分支 什么时候需要开分支&#xff1f; - 隔离线上版本和开发版本 - 大功能开发&#xff0c;不想影响到其他人&#xff0c;自己独立开个分支去开发 SVN经典目录结构&#xff1a; - trunk-------------------------开发中的文件 - bran…...

【VB语言】EXCEL中VB宏的应用

【VB语言】EXCEL中VB宏的应用 文章目录 [TOC](文章目录) 前言一、EXCEL-VB1.实验过程2.代码 二、EXCEL-VB 生成.c.h文件1.实验过程2.代码 四、参考资料总结 前言 1.WPS-VB扩展包 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、EXCEL-VB 1.实验过…...

前端优化可以从哪些方面下手及优化方案

前端优化是提升网页性能、提升用户体验和降低服务器负担的重要手段。可以从多个角度入手&#xff0c;以下是一些常见的优化方向和方案&#xff1a; 1. 性能优化 减少请求数量&#xff1a;尽量减少页面加载时发起的 HTTP 请求&#xff0c;例如使用合并文件&#xff08;CSS 和 …...

类和对象(5)——抽象类和接口

目录 1. 抽象类 1.1 抽象类的概念 1.2 抽象类语法&#xff1a;abstract关键字 1.3 抽象类的特性 1.4 抽象类的作用 2. 接口 2.1 接口的概念 2.2 接口语法&#xff1a;interface关键字 2.3 接口的实现&#xff1a;implements关键字 2.4 接口的特性 2.5 实现多个接口 …...

海康摄像头IPV6模式,手动,自动,路由公告

海康摄像头DS-2DC7220IW-A 网络设置中的IPv6配置选项。IPv6是互联网协议&#xff08;IP&#xff09;的第六版&#xff0c;用于替代IPv4&#xff0c;提供更多的IP地址和改进的网络功能。图片中的选项允许用户选择如何配置设备的IPv6网络连接&#xff1a; 手动&#xff1a;用户可…...

LabVIEW与USB设备开发

开发一台USB设备并使用LabVIEW进行上位机开发&#xff0c;涉及底层驱动的编写、USB通信协议的实现以及LabVIEW与设备的接口设计。本文将详细介绍如何开发USB设备驱动、实现LabVIEW与USB设备的通信以及优化数据传输&#xff0c;帮助用户顺利完成项目开发。下面是一个详细的说明&…...

BY组态:工业自动化的未来,触手可及

在工业4.0的浪潮下&#xff0c;智能化、数字化已成为制造业发展的核心驱动力。作为工业自动化领域的重要工具&#xff0c;组态软件在实现设备监控、数据采集、流程控制等方面发挥着不可替代的作用。然而&#xff0c;传统的组态软件往往存在开发周期长、学习成本高、灵活性不足等…...

深入理解Python多进程编程 multiprocessing

深入理解Python多进程编程 multiprocessing flyfish Python 的 multiprocessing 模块允许创建多个进程&#xff0c;从而可以利用多核处理器的能力来并行执行任务。这意味着程序的不同部分可以在不同的CPU核心上同时运行&#xff0c;极大地提高了处理效率&#xff0c;特别是在…...

使用DeepSeek建立一个智能聊天机器人0.12

为了确保这段代码能够在Windows和Linux系统上都能正常运行,我考虑以下几个方面: 路径分隔符:在Windows和Linux中,文件路径的分隔符不同。Windows使用反斜杠(\),而Linux使用正斜杠(/)。我们可以使用 os.path.join 来处理路径,以确保跨平台兼容性。 消息框:tkinter.…...

基于VLC的Unity视频播放器(三)

关于UMP插件 UMP插件不更新了&#xff0c;我测试在Ubuntu24.04上编辑器和运行时都无法正常播放&#xff0c;在替换lib之后编辑器可以播放&#xff0c;但打包后不行……很奇怪 继续更新了一下UnityVLC 添加了对Linux的支持&#xff0c;勉强都可以播放了…… Win截图 Ubuntu2…...

每日一题——把数字翻译成字符串

把数字翻译成字符串 题目描述示例示例1示例2 题解动态规划代码实现复杂度分析 总结 题目描述 有一种将字母编码成数字的方式&#xff1a;‘a’->1, ‘b’->2, … , ‘z’->26。 现在给一串数字&#xff0c;返回有多少种可能的译码结果。 数据范围&#xff1a;字符串…...

基于状态观测器和物联网基础设施的智能电网高速孤岛检测

论文标题 中文标题&#xff1a; 基于状态观测器和物联网基础设施的智能电网高速孤岛检测 英文标题&#xff1a; High-Speed Islanding Detection in Smart Grids Using a State Observer and IoT Infrastructure 作者信息 Shahid Karim<sup>1,2, *</sup>, Prajo…...

FPGA的星辰大海

编者按 时下风头正盛的DeepSeek,正值喜好宏大叙事的米国大统领二次上岗就业,OpenAI、软银、甲骨文等宣布投资高达5000亿美元“星际之门”之际,对比尤为强烈。 某种程度上,,是低成本创新理念的直接落地。 包括来自开源社区的诸多赞誉是,并非体现技术有多“超越”,而是…...

【Black Mesa】黑山起源用服务器开服多人联机教程

1、登录服务器&#xff08;百度莱卡云游戏面板&#xff09; 进入控制面板后会出现正在安装的界面&#xff0c;安装大约10分钟&#xff08;如长时间处于安装中请联系我们的客服人员&#xff09; 2、修改端口 看到一下图片的界面时说明服务器已经安装完成&#xff0c;服务器需要…...

【学习笔记】深度学习网络-深度模型中的优化

​ 作者选择了由 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 三位大佬撰写的《Deep Learning》(人工智能领域的经典教程&#xff0c;深度学习领域研究生必读教材),开始深度学习领域学习&#xff0c;深入全面的理解深度学习的理论知识。 在之前的文章中介绍了深度学习中…...

java八股文之Redis

1.Rdis常见的使用场景 缓存分布式锁&#xff08;redision&#xff0c;setnx&#xff09;计数器保存token消息队列延迟队列 2.说明一下缓存雪崩&#xff0c;缓存穿透和缓存击穿以及解决方式 1.缓存雪崩 定义&#xff1a; 缓存雪崩指的是当大量的缓存数据同时失效&#xff0c…...

ubuntu系统下KVM设置桥接网络(失败)

20250216 - 概述 因实验需求&#xff0c;需要设置KVM下的虚拟机采用桥接模式进行通信&#xff0c;这种方式将使虚拟机与主机类似使用同一网段的IP。实际上&#xff0c;为了实现这个功能&#xff0c;我已经在自己mac上VMware使用过&#xff0c;虚拟机获得了自己独立的IP。 但…...

CentOS 7操作系统部署KVM软件和创建虚拟机

CentOS 7.9操作系统部署KVM软件和配置指南&#xff0c;包括如何创建一个虚拟机。 步骤 1: 检查硬件支持 首先&#xff0c;确认您的CPU支持虚拟化技术&#xff0c;并且已在BIOS中启用&#xff1a; egrep -c (vmx|svm) /proc/cpuinfo 如果输出大于0&#xff0c;则表示支持虚拟…...

驱动开发系列38 - Linux Graphics 3D 绘制流程(一)- 创建画布

一:概述 当应用程序创建 OpenGL 上下文时,它通常需要申请帧缓冲(Framebuffer,即画布)。在 X11 体系下,应用程序不会直接向内核的 DRM 模块请求创建帧缓冲,而是通过 X 服务器进行申请。 虽然从技术上讲,应用程序可以直接使用 DRM 接口创建帧缓冲对象(BO),但为了将其与…...

Spring Boot过滤器链:从入门到精通

文章目录 一、过滤器链是什么&#xff1f;二、为什么需要过滤器链&#xff1f;三、Spring Boot中的过滤器链是如何工作的&#xff1f;&#xff08;一&#xff09;过滤器的生命周期&#xff08;二&#xff09;过滤器链的执行流程 四、如何在Spring Boot中定义自己的过滤器&#…...

QT 读写锁

一、概述 1、读写锁是一种线程同步机制&#xff0c;用于解决多线程环境下的读写竞争问题。 2、读写锁允许多个线程同时获取读锁&#xff08;共享访问&#xff09;&#xff0c;但只允许一个线程获取写锁&#xff08;独占访问&#xff09;。 3、这种机制可以提高并发性能&…...

C++中的智能指针

智能指针总结 智能指针其作⽤是管理⼀个指针&#xff0c;避免程序员申请的空间在函数结束时忘记释放&#xff0c;造成内存泄漏这种情况滴发⽣。使⽤智能指针可以很⼤程度上的避免这个问题&#xff0c;因为智能指针就是⼀个类&#xff0c;当超出了类的作⽤域是&#xff0c;类会…...

IntelliJ IDEA 接入 AI 编程助手(Copilot、DeepSeek、GPT-4o Mini)

IntelliJ IDEA 接入 AI 编程助手&#xff08;Copilot、DeepSeek、GPT-4o Mini&#xff09; &#x1f4ca; 引言 近年来&#xff0c;AI 编程助手已成为开发者的高效工具&#xff0c;它们可以加速代码编写、优化代码结构&#xff0c;并提供智能提示。本文介绍如何在 IntelliJ I…...

【R语言】非参数检验

一、Mann-Whitney检验 在R语言中&#xff0c;Mann-Whitney U检验&#xff08;也称为Wilcoxon秩和检验&#xff09;用于比较两个独立样本的中位数是否存在显著差异。它是一种非参数检验&#xff0c;适用于数据不满足正态分布假设的情况。 1、独立样本 # 创建两个独立样本数据…...

PyTorch 源码学习:阅读经验 代码结构

分享自己在学习 PyTorch 源码时阅读过的资料。本文重点关注阅读 PyTorch 源码的经验和 PyTorch 的代码结构。因为 PyTorch 不同版本的源码实现有所不同&#xff0c;所以笔者在整理资料时尽可能按版本号升序&#xff0c;版本号见标题前[]。最新版本的源码实现还请查看 PyTorch 仓…...