JVM-垃圾回收
什么是JVM的垃圾回收?
垃圾回收(Garbage Collection, GC)是Java虚拟机(JVM)自动管理内存的一种机制,其目的是自动回收不再使用的对象所占用的内存空间,以防止内存泄漏和提升内存利用效率。
运行时数据区
运行时数据区即JVM的内存区域,其中包含程序计数器、java虚拟机栈、本地方法栈、方法区、堆区。
其中,程序计数器、java虚拟机栈、本地方法栈线程私有,所占内存会随线程的结束而回收,因此在大多数情况下,不需要特别关注它们的垃圾回收。
方法区和堆区是线程共享的,垃圾回收的重点主要在这两个区域,尤其是堆区。
方法区的垃圾回收
方法区的主要内容
- 类元数据:存储类的结构信息,如字段、方法、接口等。
- 运行时常量池:存储类文件中的常量池(包括字面量和符号引用)的运行时表示。
- 静态变量:类的静态字段,属于类而非实例。
- 即时编译器编译后的代码:存储JIT编译器生成的本地代码。
垃圾回收的主要目标
1. 类卸载
- 原因:当一个类加载器被卸载时,所有由该加载器加载的类也应该被卸载,以释放内存。
- 条件:要卸载一个类,必须确保该类的所有实例都已经被回收,并且没有对该类的任何引用(包括反射引用)存在。
- 过程(可达性分析):
- 标记阶段:标记所有可以访问的类。
- 清除阶段:清除没有标记到的类,释放其占用的内存。
- 困难:类卸载相对较为复杂,因为需要确保没有剩余的引用,这在长时间运行的应用中可能较难达成。
2. 运行时常量池回收
- 原因:运行时常量池中的常量可能会大量增加,特别是字符串常量和符号引用。
- 过程(可达性分析):
- 标记阶段:标记所有可以访问的常量。
- 清除阶段:清除未被标记的常量,释放内存。
引用计数法和可达性分析法
1.引用计数法
引用计数法会为每个对象维护一个引用计数器,当对象被引用时加1,取消引用时减1。
引用计数法的优点是实现简单,C++中的智能指针就采用了引用计数法,但是它也存在缺点,主要有两点:
- 每次引用和取消引用都需要维护计数器,对系统性能会有一定的影响
- 存在循环引用问题,所谓循环引用就是当A引用B,B同时引用A时会出现对象无法回收的问题。
2.可达性分析法
可达性分析法(Reachability Analysis)是判断对象是否可被回收的一种算法,它通过一系列称为“GC Roots”的对象作为起点,沿着这些起点引用的路径进行遍历。如果一个对象在遍历过程中不可达,即没有任何引用链能从GC Roots到达该对象,则该对象被认为是不可达的,可以被回收。
GC Roots包括:
-
活动线程:
- 所有存活的线程对象,包括各个线程的栈帧中的本地变量表中的引用,即当前活动线程的所有局部变量。
-
方法区中的静态引用:
- 类静态字段引用的对象,即由方法区中的类的静态变量所引用的对象。
-
方法区中的常量引用:
- 运行时常量池中的常量引用,例如字符串常量池中的字符串对象。
-
本地方法栈中的引用:
- JNI中的本地引用,包括JNI接口通过
NewGlobalRef
创建的全局引用。
- JNI中的本地引用,包括JNI接口通过
-
同步锁定的对象:
- 当前被锁定的对象,即被同步块或方法锁定的对象,因为这些对象在持有者线程中被引用。
这些GC Roots构成了对象图中最初始的可达节点,从这些节点开始,垃圾回收器将追踪所有可达对象。如果一个对象未能从这些GC Roots到达,则说明它是不可达的,可以被垃圾回收。每次GC都会从这些根节点出发,确定哪些对象存活并需要保留,哪些对象可以被回收以释放内存
方法区垃圾回收的特点
- 回收频率低:方法区的垃圾回收频率低于堆区,因为方法区中的数据变动相对较少。
- 回收复杂度高:类卸载和常量池的回收需要精确管理引用,确保没有遗漏,复杂度较高。
- 影响性能:方法区的垃圾回收可能会对性能产生较大影响,尤其是在需要频繁加载和卸载类的情况下。
JVM对方法区垃圾回收的支持
- 永久代(PermGen):在JDK 7及之前,方法区位于永久代。永久代的垃圾回收由Full GC执行。
- 元空间(Metaspace):在JDK 8及之后,方法区改为元空间。元空间是直接使用本地内存而不是堆内存,垃圾回收机制有所改进。
总结
方法区的垃圾回收虽然不如堆区频繁,但依然是JVM内存管理的重要部分,尤其是在长时间运行和高动态性的应用中。主要涉及类卸载和常量池的回收,确保内存的有效利用和防止内存泄漏。
常见的引用方式
可达性算法中描述的对象引用,一般指的是强引用,即是GCRoot对象对普通对象有引用关系,只要这层关系存在,普通对象就不会被回收。除了强引用之外,Java中还设计了几种其他引用方式:
-
软引用
-
弱引用
-
虚引用
-
终结器引用
软引用
软引用相对于强引用是一种比较弱的引用关系,如果一个对象只有软引用关联到它,当程序内存不足时,就会将软引用中的数据进行回收。在JDK 1.2版之后提供了SoftReference类来实现软引用,软引用常用于缓存中。
这样做有什么好处?如果对象A是一个缓存,平时会保存在内存中,如果想访问数据可以快速访问。但是如果内存不够用了,我们就可以将这部分缓存清理掉释放内存。即便缓存没了,也可以从数据库等地方获取数据,不会影响到业务正常运行,这样可以减少内存溢出产生的可能性。
特别注意:
软引用对象本身,也需要被强引用,否则软引用对象也会被回收掉。
软引用的使用方法
软引用的执行过程如下:
1.将对象使用软引用包装起来,new SoftReference<对象类型>(对象)。
2.内存不足时,虚拟机尝试进行垃圾回收。
3.如果垃圾回收仍不能解决内存不足的问题,回收软引用中的对象。
4.如果依然内存不足,抛出OutOfMemory异常。
软引用对象本身怎么回收呢?
如果软引用对象里边包含的数据已经被回收了,那么软引用对象本身其实也可以被回收了。
SoftReference提供了一套队列机制:
- 软引用创建时,通过构造器传入引用队列
- 在软引用中包含的对象被回收时,该软引用对象会被放入引用队列
- 通过代码遍历引用队列,将SoftReference的强引用删除
示例:
public class SoftReferenceDemo3 {public static void main(String[] args) throws IOException {ArrayList<SoftReference> softReferences = new ArrayList<>();ReferenceQueue<byte[]> queues = new ReferenceQueue<byte[]>();for (int i = 0; i < 10; i++) {byte[] bytes = new byte[1024 * 1024 * 100];SoftReference studentRef = new SoftReference<byte[]>(bytes,queues);softReferences.add(studentRef);}SoftReference<byte[]> ref = null;int count = 0;while ((ref = (SoftReference<byte[]>) queues.poll()) != null) {count++;}System.out.println(count);}
}
弱引用
弱引用的整体机制和软引用基本一致,区别在于弱引用包含的对象在垃圾回收时,不管内存够不够都会直接被回收。在JDK 1.2版之后提供了WeakReference类来实现弱引用,弱引用主要在ThreadLocal中使用。
虚引用和终结器引用
这两种引用在常规开发中是不会使用的。
-
虚引用也叫幽灵引用/幻影引用,不能通过虚引用对象获取到包含的对象。虚引用唯一的用途是当对象被垃圾回收器回收时可以接收到对应的通知。Java中使用PhantomReference实现了虚引用,直接内存中为了及时知道直接内存对象不再使用,从而回收内存,使用了虚引用来实现。
-
终结器引用指的是在对象需要被回收时,终结器引用会关联对象并放置在Finalizer类中的引用队列中,在稍后由一条由FinalizerThread线程从队列中获取对象,然后执行对象的finalize方法,在对象第二次被回收时,该对象才真正的被回收。在这个过程中可以在finalize方法中再将自身对象使用强引用关联上,但是不建议这样做。
堆区的垃圾回收
堆区的垃圾回收(GC)是Java虚拟机(JVM)内存管理的核心部分,涉及到清理不再被使用的对象以释放内存。
1. 堆区的内存结构
堆区是JVM内存的一部分,专门用于存储所有对象实例。通常堆区被分为以下几个区域:
-
年轻代(Young Generation):
- 存储新创建的对象。
- 通常分为三个部分:
- Eden区:对象首先分配到Eden区。
- Survivor区(S0和S1):Eden区经过垃圾回收后,存活的对象会被移动到Survivor区的一个区域。Survivor区有两个部分(S0和S1),它们交替使用。
-
老年代(Old Generation):
- 存储经过多次垃圾回收仍然存活的对象。
- 老年代的垃圾回收频率较低,因为对象在这里的生命周期较长。
-
永久代(PermGen)(Java 8之前版本存在,后被元空间取代,元空间存在于直接内存,不在堆中):
- 存储类元数据、常量池、方法区等信息的一部分内存区域。永久代存在于JVM堆内存中,但其管理和内存回收机制与堆区的其他部分有所不同。
2. 垃圾回收算法
垃圾回收器使用不同的算法来管理内存。常见的算法包括:
-
标记-清除算法(Mark-and-Sweep):
- 标记:从GC Roots开始,遍历并标记所有可达对象。
- 清除:回收所有未被标记的对象。
- 优点:简单直观。
- 缺点:清除后会产生内存碎片,可能导致内存空间不连续。
-
复制算法(Copying):
- .准备两块空间From空间和To空间,每次在对象分配阶段,只能使用其中一块空间(From空间),.在垃圾回收GC阶段,将From中存活对象复制到To空间,将两块空间的From和To名字互换。
- 优点:高效的垃圾回收,减少了碎片。
- 缺点:需要额外的内存空间作为备用区域。
-
标记-整理算法(Mark-Compact):
- 结合了标记-清除和复制算法的优点。首先标记所有可达对象,然后将这些对象整理到内存的一端,最后清理未被使用的内存。
- 优点:消除了碎片问题。
- 缺点:整理过程可能比较复杂和耗时。
-
分代收集算法(Generational Collection):
- 根据对象的年龄将堆区划分为不同的代。年轻代和老年代使用不同的回收算法,年轻代通常使用复制算法,而老年代使用标记-整理算法。
- 优点:提高了回收效率,减少了老年代GC的频率。
- 缺点:管理多个代的复杂性。
3. 垃圾回收器
由于垃圾回收器分为年轻代和老年代,除了G1之外其他垃圾回收器必须成对组合进行使用。具体的关系图如下:
1. 年轻代-Serial垃圾回收器
Serial是是一种单线程串行回收年轻代的垃圾回收器。
-
使用的算法:
- 年轻代:复制算法。
-
优点:
- 单CPU处理器下吞吐量非常出色
-
缺点:
- 多CPU下吞吐量不如其他垃圾回收器,堆如果偏大会让用户线程处于长时间的等待
-
适用场景:
- Java编写的客户端程序或者硬件配置有限的场景
2. 老年代-SerialOld垃圾回收器
SerialOld是Serial垃圾回收器的老年代版本,采用单线程串行回收
-XX:+UseSerialGC 新生代、老年代都使用串行回收器。
-
使用的算法:
- 老年代:标记-整理算法
-
优点:
- 单CPU处理器下吞吐量非常出色
-
缺点:
- 多CPU下吞吐量不如其他垃圾回收器,堆如果偏大会让用户线程处于长时间的等待
-
适用场景:
- 与Serial垃圾回收器搭配使用,或者在CMS特殊情况下使用
3. 年轻代-ParNew垃圾回收器
ParNew垃圾回收器本质上是对Serial在多CPU下的优化,使用多线程进行垃圾回收
-XX:+UseParNewGC 新生代使用ParNew回收器, 老年代使用串行回收器
-
使用的算法:
- 年轻代:复制算法。
-
优点:
- 多CPU处理器下停顿时间较短
-
缺点:
- 吞吐量和停顿时间不如G1,所以在JDK9之后不建议使用
-
适用场景:
- JDK8及之前的版本中,与CMS老年代垃圾回收器搭配使用
4. 老年代- CMS(Concurrent Mark Sweep)垃圾回收器
CMS垃圾回收器关注的是系统的暂停时间,允许用户线程和垃圾回收线程在某些步骤中同时执行,减少了用户线程的等待时间。
参数:XX:+UseConcMarkSweepGC
-
使用的算法:
- 老年代:标记清除算法
-
优点:
- 系统由于垃圾回收出现的停顿时间较短,用户体验好
-
缺点:
1、内存碎片问题
2、退化问题
3、浮动垃圾问题
-
适用场景:
- 大型的互联网系统中用户请求数据量大、频率高的场景,比如订单接口、商品接口等
-
CMS执行步骤:
1.初始标记,用极短的时间标记出GC Roots能直接关联到的对象。
2.并发标记, 标记所有的对象,用户线程不需要暂停。
3.重新标记,由于并发标记阶段有些对象会发生了变化,存在错标、漏标等情况,需要重新标记。
4.并发清理,清理死亡的对象,用户线程不需要暂停。
5. 年轻代-Parallel Scavenge垃圾回收器
Parallel Scavenge是JDK8默认的年轻代垃圾回收器,多线程并行回收,关注的是系统的吞吐量。具备自动调整堆内存大小的特点。
-
使用的算法:
- 年轻代:复制算法。
-
优点:
- 吞吐量高,而且手动可控。为了提高吞吐量,虚拟机会动态调整堆的参数
-
缺点:
- 不能保证单次的停顿时间
-
适用场景:
- 后台任务,不需要与用户交互,并且容易产生大量的对象。比如:大数据的处理,大文件导出
-
常用参数:
Parallel Scavenge允许手动设置最大暂停时间和吞吐量。Oracle官方建议在使用这个组合时,不要设置堆内存的最大值,垃圾回收器会根据最大暂停时间和吞吐量自动调整内存大小。
-
最大暂停时间,
-XX:MaxGCPauseMillis=n
设置每次垃圾回收时的最大停顿毫秒数 -
吞吐量,
-XX:GCTimeRatio=n
设置吞吐量为n(用户线程执行时间 = n/n + 1) -
自动调整内存大小,
-XX:+UseAdaptiveSizePolicy
设置可以让垃圾回收器根据吞吐量和最大停顿的毫秒数自动调整内存大小
-
6. 老年代-Parallel Old垃圾回收器
Parallel Old是为Parallel Scavenge收集器设计的老年代版本,利用多线程并发收集。
参数: -XX:+UseParallelGC 或
-XX:+UseParallelOldGC可以使用Parallel Scavenge + Parallel Old这种组合。
-
使用的算法:
- 老年代:标记-整理算法
-
优点:
- 并发收集,在多核CPU下效率较高
- 缺点:
- 暂停时间会比较长
-
适用场景:
- 与Parallel Scavenge配套使用
7. G1 – Garbage First 垃圾回收器
参数1: -XX:+UseG1GC
打开G1的开关,JDK9之后默认不需要打开
参数2:-XX:MaxGCPauseMillis=毫秒值
最大暂停的时
- 回收年代和算法:
- 年轻代+老年代
- 复制算法
-
优点:
- 对比较大的堆如超过6G的堆回收时,延迟可控
- 不会产生内存碎片
- 并发标记的SATB算法效率高
- 缺点:
- JDK8之前还不够成熟
-
适用场景:
- JDK8最新版本、JDK9之后建议默认使用
总结
垃圾回收器的组合关系虽然很多,但是针对几个特定的版本,比较好的组合选择如下:
JDK8及之前:
ParNew + CMS(关注暂停时间)、Parallel Scavenge + Parallel Old (关注吞吐量)、 G1(JDK8之前不建议,较大堆并且关注暂停时间)
JDK9之后:
G1(默认)
相关文章:

JVM-垃圾回收
什么是JVM的垃圾回收? 垃圾回收(Garbage Collection, GC)是Java虚拟机(JVM)自动管理内存的一种机制,其目的是自动回收不再使用的对象所占用的内存空间,以防止内存泄漏和提升内存利用效率。 运…...

8.C基础_指针基础
指针概述 1、定义与初始化 形式:<数据类型>* <变量名> <地址>; int a 10; int *p &a; 指针的类型不同,p时的偏移地址量不同,偏移地址 sizeof(类型)Byte 注意点: 指针的类型要与数据的类型保持一…...

安泰功率放大器使用注意事项有哪些内容和要求
功率放大器是一种广泛应用于音频系统、通信系统和实验室等领域的设备,其作用是将输入信号放大到足够大的功率输出。为了安全使用功率放大器并保证其性能和寿命,以下是一些使用功率放大器时需要注意的事项和要求。 一、选择适当的功率放大器: …...

windows下,使用vs code远程连接云服务器【以CentOS7为例】
windows下,使用vs code远程连接云服务器 1. 下载VS code并准备相关插件2. 使用Remote - SSH远程控制Linux 1. 下载VS code并准备相关插件 1. VS code官网: 下载地址:https://code.visualstudio.com/Download下载合适版本的vs code。 2. 推荐…...

x264 中像素扩边算法源码分析
介绍 在视频编码中,特别是在使用x264这样的H.264/MPEG-4 AVC编码器时,像素扩边(或称为边缘扩展)是一项重要的预处理步骤。像素扩边的目的是在帧的边缘添加额外的像素,这样在编码过程中可以应用滤波器,如去块滤波器(deblocking filter)和水平/垂直滤波器,而不会受到帧…...

数据结构——双链表详解(超详细)
前言: 小编在之前已经写过单链表的创建了,接下来要开始双链表的讲解了,双链表比单链表要复杂一些,不过确实要比单链表更好进行实现!下面紧跟小编的步伐,开启今天的双链表之旅! 目录 1.概念和结构…...

银行项目利润问题(贪心思想)
import java.util.Comparator; import java.util.PriorityQueue;public class test32 {//输入正数数组costs、正数数组profits、正数K、正数M//costs[i]表示i号项目花费,profits[i]表示i号项目在扣除花费后还挣的钱//K表示有多少项目//M表示初始资金//每做完一个项目…...

SQLite
SQLite Insert 插入 语句 方式1: INSERT INTO TABLE_NAME [(column1, column2, column3,...columnN)] VALUES (value1, value2, value3,...valueN); 方式2: INSERT INTO TABLE_NAME VALUES (value1,value2,value3,...valueN); (如果要…...

浅谈 Mybatis 框架
文章目录 一、什么是MyBatis?1.2、JDBC 二、使用Mybatis2.1、配置MyBatis开发环境2.1.1、配置连接字符串2.1.2、配置MyBatis中的XML路径 2.2、使用MyBatis模式和语法操作数据库 三、使用 Mybatis 进行增删改查操作的要点3.1、ResultMap的用法 四、Mybatis操作难点4.1、#{ } 和…...

【星海随笔】OSPF协议
OSPF OSPF 可在单一自治系统(Autonomous System, AS)内决策路由。OSPF 是目前内部网关协议中使用最为广泛、性能最优的一个动态路由协议。 (1) OSPF 的特点。可适应大规模的网,路由变化收敛速度块,无路由自环,支持变…...

Vue 使用elementUI-plus el-calendar加 公历转农历 是否节假日 等
效果图: 1. 使用到自定文件 calendar.js /*** 1900-2100区间内的公历、农历互转* charset UTF-8* Author Jea杨(JJonlineJJonline.Cn)* Time 2014-7-21* Time 2016-8-13 Fixed 2033hex、Attribution Annals* Time 2016-9-25 Fixed lunar LeapMonth Param…...

SQL-锁
一.锁的介绍 锁是计算机协调多个进程或线程并发访问一资源的机制。在数据中,除传统的计算资源(CPU,RAM,I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性,有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因…...

索引小tips
一、优化原则 关于创建索引: 1. 【强制】InnoDB表必须主键为id int/bigint auto_increment,且主键值禁⽌被更新 。 2. 【强制】InnoDB和MyISAM存储引擎表,索引类型必须为 BTREE 。 3. 【建议】主键的名称以 pk 开头,唯⼀键以…...

2024年【中级消防设施操作员(考前冲刺)】报名考试及中级消防设施操作员(考前冲刺)免费试题
题库来源:安全生产模拟考试一点通公众号小程序 中级消防设施操作员(考前冲刺)报名考试是安全生产模拟考试一点通生成的,中级消防设施操作员(考前冲刺)证模拟考试题库是根据中级消防设施操作员(…...

数据结构:栈(含源码)
目录 一、栈的概念和结构 二、栈的实现 2.1 头文件 2.2 各个功能的实现 初始化栈 入栈 出栈 获取栈顶元素和栈中有效个数 判断栈是否为空 栈的销毁 2.3 测试 完整源码 一、栈的概念和结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和…...
如何使用Markdown编辑器
欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持&#x…...

当代最火的哲学家颜廷利:全球公认十个最厉害的思想家之一
颜廷利书法特点和艺术成就:全球公认十个最厉害的思想家之一,颜廷利教授是一位杰出的书法家,他的书法作品不仅体现了中国传统文化,而且在国内外享有高度评价,对当代书法艺术产生了深远的影响。在中国十大顶级哲学家排行榜上,当今世界最重要的思想家颜廷利教授的书…...

android13内核增加调试接口给上层使用
总纲 android13 rom 开发总纲说明 目录 1.前言 2.处理方法分析 3.代码参考 3.1方法1 3.2方法2 3.3方法3 3.4方法4 4.彩蛋 1.前言 有时候,我们在开机的过程中,adb服务还没有起来,系统奔溃了,不能正常开机,我们没法看到相关的logcat信息,导致我们不能很快的定…...

linux:phpstudy安装及日常命令使用[表格]
官网安装:小皮面板下载安装,一键管理服务器-小皮面板 (xp.cn) centos安装: yum install -y wget && sudo wget -O install.sh https://dl.xp.cn/dl/xp/install.sh && sudo bash install.sh 快速使用 [rootlocalhost ~]# …...

【python】Linux升级版本
目的 迁移项目包路径到服务器上 查看服务器包是否和本地已有项目python版本相同然后发现~嗯不一样 项目上包时用的3.8~ 服务器用的2.7 查看方法: python -version解决方案 一:项目所有包重新下载 二:升级服务器python版本 第二种步骤&…...

鸿蒙开发if判断有点坑
它的判断和Android的有点不同,归结到底不是同一种语言,数据类型不一样 if (0) {logContent("aa","0") } else {logContent("aa","00")...

IT课程学习搭子
各种IT课程齐全可学,价格你绝对想不到,相比于培训班有以下优势: 1、避免被割韭菜,避免踩坑,避免交智商税,最低的成本学最有价值的课,同时又能达到比培训班更好的效果 2、收徒,带你学…...

hive拼接字符串concat函数的用法
在 Hive 中,字符串拼接是一种常见的操作,用于将多个字符串连接在一起形成一个新的字符串。这在数据处理和分析过程中经常会用到,比如将不同列的值拼接成一个完整的信息、拼接成文件路径等等。 字符串拼接函数 在 Hive 中,我们可…...

Linux-理解shell
文章目录 5. 理解shell5.1 shell的类型5.2 交互shell和系统默认shell5.3 安装zsh shell程序5.4 shell的父子关系5.5 命令列表5.6 命令分组5.7 使用命令分组创建子shell5.8 子shell用法5.9 shell的非内建命令和内建命令5.9.1 非内建命令5.9.2 内建命令5.9.3 history和alias命令介…...

FutureTask详解
目录 FutureTask详解1、FutureTask简介2、FutureTask内部结构继承结构类属性构造方法内部类WaitNode 3、Runnable、Callable、Future、RunnableFuture接口①、Runnable接口②、Callable接口③、Future接口④、RunnableFuture接口总结对比 4、FutureTask的使用示例普通Thread使用…...

javase综合案例4 -- 考试系统
文章目录 一,项目要求二,创建实体类ExamItem三,创建考试服务类ExamService3.1 全局变量 考题列表itemList(List< ExamItem >类型),答案数组answerArr (String[]类型),得分score3.2 初始化方法init()3.3 打印菜单…...

Logistic回归
Logistic回归模型: 适用于二分类或多分类问题,样本特征是数值型(否则需要转换为数值型) 策略:极大似然估计 算法:随机梯度 或 BFGS算法(改进的拟牛顿法) 线性回归表达式…...

Langchain-Chatchat+Xinference集成部署
Langchain-ChatchatXinference集成部署 安装环境: 系统:Anolis OS 8.9 python版本:Python 3.9.19 Langchain-Chatchat版本:0.3.1.3 Xinference版本:v0.13.3 模型选择(下载时需要科学上网)&#…...

江协科技51单片机学习- p33 PWM呼吸灯和直流驱动电机调速
🚀write in front🚀 🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝…...

使用Jetbrains.Rider反编译Unity的DLL文件看源码
直接将dll文件的打开方式用Rider打开即可,打开BattleSeqGenertor.dll文件的效果如下:...