【JVM】JVM基础教程(四)
上一章:【JVM】JVM基础教程(三)-CSDN博客
目录
自动垃圾回收
方法区的回收
方法区回收条件
手动触发回收
堆回收
如何判断堆上的对象可以回收?
可以给对象引用赋值null,切断引用
引用计数法
循环引用缺点
查看垃圾回收日志
可达性分析法
MAT查看GC Root
五种对象引用
强引用
软引用
队列机制
使用场景-缓存
弱引用
虚引用和终结器引用
总结
垃圾回收算法
核心思想
垃圾回收算法的历史和分类
评价标准
标记清楚算法
复制算法
标记整理算法
分代垃圾回收算法
arthas查看分代之后的内存情况
调整内存区域的大小
垃圾回收器
为什么分代GC算法要把堆分成年轻代和老年代?
垃圾回收器的组合关系

自动垃圾回收
C/C++的内存管理
在C/C++这类没有自动垃圾回收机制的语言中,一个对象如果不再使用,需要手动释放,否则就会出现 内存泄漏。我们称这种释放对象的过程为垃圾回收,而需要程序员编写代码进行回收的方式为手动回收。
内存泄漏指的是不再使用的对象在系统中未被回收,内存泄漏的积累可能会导致内存溢出。

Java的内存管理
Java中的简化对象的释放,引入了自动的垃圾回收(Garbage Collection简称GC)机制。通过垃 圾回收器来对不再使用的对象完成自动的回收,垃圾回收器主要负责对堆上的内存进行回收。其他 很多现代语言比如C#、Python、Go都拥有自己的垃圾回收器(所以也算不上Java的优势了,在Java刚出的时候倒是个优势)

垃圾回收的对比

自动垃圾回收的应用场景

方法区的回收

Java的内存管理
线程不共享的部分,都是伴随着线程的创建而创建,线程的销毁而销毁。而方法的栈帧在执行完方法之后就会自动弹出栈并释放掉对应的内存


继续说方法区的回收
方法区回收条件
方法区中能回收的内容主要就是不再使用的类
判定一个类是否可以被卸载,需要同时满足下面三个条件
- 此类所有实例对象都已经被收回,在堆中不存在任何类的实例对象以及子类对象
- 加载该类的类加载器已经被回收
- 该类对应的 java.lang.Class 对象没有在任何地方被引用



手动触发回收
- 如果需要手动触发垃圾回收,可以调用System.gc()方法。
- 语法:System.gc()
- 注意事项:
调用System.gc()方法并不一定会立即回收垃圾,仅仅是向Java虚拟机发送一个垃圾回收的请求,具体是否需要 执行垃圾回收Java虚拟机会自行判断
开发中此类场景一般很少出现,主要在如 OSGi、JSP 的热部署等应用场景中。 每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类 加载器。重新创建类加载器,重新加载jsp文件。
堆回收
如何判断堆上的对象可以回收?
Java中的对象是否能被回收,是根据对象是否被引用来决定的,如果对象被引用了,说明该对象还在使用,不允许被回收。
比如下面代码的内存结构图


可以给对象引用赋值null,切断引用


引用计数法
话说回来,GC判断堆上的对象有没有被引用,常用的两种方法:引用计数法 和 可达性分析法
这里插一句,Java只用可达性分析法
引用计数法会为每个对象维护一个引用计数器,当对象被引用时加1,取消引用时减1

循环引用缺点
引用计数法的优点是实现简单,C++中的智能指针就采用了引用计数法,但是它也存在缺点,主要有两点:
1.每次引用和取消引用都需要维护计数器,对系统性能会有一定的影响
2.存在循环引用问题,所谓循环引用就是当A引用B,B同时引用A时会出现对象无法回收的问题。
为什么会导致循环引用呢?我查了一下资料
根据定义,a引用了b,b的引用计数+1,b引用了a,a的引用计数+1
正常情况下,a b两个都赋值null,就会将他俩的引用技术 -1,这样就能回收了
不过,由于a b互相引用,所以即使赋值null,他俩都不会 -1,会维持1
所以我猜测,应该是判断互相引用的优先级高与赋值null

查看垃圾回收日志
- 如果想要查看垃圾回收的信息,可以使用-verbose:gc参数
- 语法:-verbose:gc

java -verbose:gc -jar your-application.jar
可达性分析法
Java使用的是可达性分析算法来判断对象是否可以被回收。
可达性分析将对象分为两类:
垃圾回收的根对象(GC Root)和普通对象
对象与对象之间存在引用关系
下图中A到B再到C和D,形成了一个引用链,可达性分析算法指的是如果从某个普通对象到GC Root对象是可达的,对象就不可被回收

哪些对象被称之为GC Root对象呢?
- 线程Thread对象,引用线程栈帧中的方法参数,局部变量等。
- 系统类加载器(也就是应用程序类加载器)加载的java.lang.Class对象,引用类中的静态变量
- 监视器对象,用来保存同步锁synchronized关键字持有的对象
- 本地方法调用时使用的全局对象



MAT查看GC Root


下载eclipse Memory Analyzer(MAT)工具
Eclipse downloads - Select a mirror | The Eclipse Foundation

启动后会提示切换JDK版本17+,其实是JVM版本要求

切换版本

看我另一篇博客:【Java】win10开发环境安装两个JDK【v8/17】_开发环境配置2种jdk版本-CSDN博客

就长这样
然后我们先用arthas生成堆内存快照
五种对象引用
几种常见的对象引用
- 强引用
- 软引用
- 弱引用
- 虚引用
- 终结器引用
强引用
可达性算法中描述的对象引用,一般指的是强引用
即是GCRoot对象对普通对象有引用关系,只要这层关系存在,普通对象就不会被回收。
软引用
软引用相较于强引用是一种比较弱的引用关系,如果一个对象只有软引用关联到它,当程序内存不足时,就会将软引用中的数据进行回收
在JDK1.2版之后提供了SoftReference类来实现软引用,软引用常用于缓存中

软引用的执行过程如下:
1.将对象使用软引用包装起来,new SoftReference(对象)。
2.内存不足时,虚拟机尝试进行垃圾回收。
3.如果垃圾回收仍不能解决内存不足的问题,回收软引用中的对象。
4.如果依然内存不足,抛出OutOfMemory异常。

队列机制
软引用中的对象如果在内存不足时回收,SoftReference对象本身也需要被回收。
如何知道哪些SoftReference对 象需要回收呢?
SoftReference提供了一套队列机制:
1、软引用创建时,通过构造器传入引用队列
2、在软引用中包含的对象被回收时,该软引用对象会被放入引用队列
3、通过代码遍历引用队列,将SoftReference的强引用删除

软引用也可以使用继承自SoftReference类的方式来实现,StudentRef类就是一个软引用对象。
使用场景-缓存
软引用也可以使用继承自SoftReference类的方式来实现,StudentRef类就是一个软引用对象。 通过构造器传入软引用包含的对象,以及引用队列。


弱引用
弱引用的整体机制和软引用基本一致,区别在于弱引用包含的对象在垃圾回收时,不管内存够不够都会直接被回收。
在JDK1.2版之后提供了WeakReference类来实现弱引用,弱引用主要在ThreadLocal中使用
弱引用对象本身也可以使用引用队列进行回收

虚引用和终结器引用
- 这两种引用在常规开发中是不会使用的。
- 虚引用也叫幽灵引用/幻影引用,不能通过虚引用对象获取到包含的对象。虚引用唯一的用途是当对象被垃圾回收器回收时可以接收到对应的通知。Java中使用PhantomReference实现了虚引用,直接内存中为了及时知道直接内存不再使用,从而回收内存,使用了虚引用来实现
- 终结器引用指的是在对象需要被回收时,终结器引用会关联对象并放置在Finalizer类中的引用队列中,在稍后由一条由FinalizerThread线程从队列中获取对象,然后执行对象的finalize方法,在对象第二次被回收时,该 对象才真正的被回收。在这个过程中可以在finalize方法中再将自身对象使用强引用关联上,但是不建议这样做。
总结
-
强引用(Strong Reference)
—— 普通对象引用,只有在没有任何强引用指向对象时,垃圾回收器才会回收。 -
软引用(Soft Reference)
—— 用于描述有用但非必需的对象,内存不足时会被回收,可用来实现缓存。 -
弱引用(Weak Reference)
—— 用于描述非必需对象,在下一次垃圾回收时无论内存是否充足都会被回收。 -
虚引用(Phantom Reference)
—— 不决定对象的生命周期,仅用于跟踪对象被垃圾回收的状态。 -
引用队列(Reference Queue)
—— 与软引用、弱引用和虚引用配合使用,用于检测对象何时被垃圾回收。
垃圾回收算法
核心思想
Java是如何实现垃圾回收的呢?简单来说,垃圾回收要做的有两件事:
1、找到内存中存活的对象
2、释放不再存活对象的内存,使得程序能再次利用这部分空间

垃圾回收算法的历史和分类
1960年John McCarthy发布了第一个GC算法:标记-清除算法。
1963年Marvin L. Minsky 发布了复制算法。
本质上后续所有的垃圾回收算法,都是在上述两种算法的基础上优化而来。

评价标准
垃圾回收算法的评价标准
STW
Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所 有的用户线程。这个过程被称之为Stop The World简称STW,如果STW时间过长则会影响用户的使用。

所以判断GC算法是否优秀,可以从三个方面来考虑:
- 吞吐量
- 最大暂停时间
- 堆使用效率
1.吞吐量
吞吐量指的是 CPU 用于执行用户代码的时间与 CPU 总执行时间的比值,
即吞吐量 = 执行用户代码时间 / (执行用户代码时间 + GC时间)。
吞吐量数值越高,垃圾回收的效率就越高

2.最大暂停时间
最大暂停时间指的是所有在垃圾回收过程中的STW时间最大值。
比如如下的图中,黄色部分的STW就是最 大暂停时间,显而易见上面的图比下面的图拥有更少的最大暂停时间。
最大暂停时间越短,用户使用系统时 受到的影响就越短。

3.堆使用效率
不同垃圾回收算法,对堆内存的使用方式是不同的。比如标记清除算法,可以使用完整的堆内存。而复制算 法会将堆内存一分为二,每次只能使用一半内存。从堆使用效率上来说,标记清除算法要优于复制算法。
上述三种评价标准:堆使用效率、吞吐量,以及最大暂停时间不可兼得。
一般来说,堆内存越大,最大暂停时间就越长。想要减少最大暂停时间,就会降低吞吐量。
堆内存越大,最大暂停时间越长的原因
垃圾回收范围扩大
垃圾回收需要扫描整个堆,堆越大,垃圾回收器需要检查的对象数量越多,回收所需时间越长。对象存活率的影响
对象可能在堆中存在多个代际(如年轻代、老年代等),堆越大,老年代的容量通常也会增大,而老年代回收(如Full GC)通常是一次性回收整个区域,导致停顿时间显著增加。标记和清理的开销增加
垃圾回收的标记、清理和压缩操作对堆大小敏感,堆越大,这些操作的复杂度和执行时间都可能显著增加。
减少最大暂停时间会降低吞吐量的原因
更多的小型GC代价
为了减少最大暂停时间,垃圾回收器通常采用增量回收或分区回收(如G1 GC),这些方法将一次性的大型回收分解为多次小型回收,增加了垃圾回收的频率。分而治之的开销
一些垃圾回收算法(如并发标记-清理算法)会在应用线程和回收线程之间并发运行,这会占用部分CPU资源,减少用于执行应用程序的时间,从而降低吞吐量。更频繁的Minor GC
减少最大暂停时间可能需要更小的年轻代(Young Generation),这会导致年轻代垃圾回收(Minor GC)更频繁地触发,从而增加总体回收开销。
不同的垃圾回收算法,适用于不同的场景。需要做出权衡

标记清楚算法
标记清除算法的核心思想分为两个阶段:
1.标记阶段,将所有存活的对象进行标记。Java中使用可达性分析算法,从GC Root开始通过引用链遍历出 所有存活对象(遍历一次)
2.清除阶段,从内存中删除没有被标记也就是非存活对象(遍历一次)

优缺点
优点:实现简单,只需要在第一阶段给每个对象维护标志位,第二阶段删除对象即可。
缺点:1.碎片化问题
由于内存是连续的,所以在对象被删除之后,内存中会出现很多细小的可用内存单元。如果我们需要的是一 个比较大的空间,很有可能这些内存单元的大小过小无法进行分配。

缺点:2.分配速度慢
由于内存碎片的存在,需要维护一个空闲链表,极有可能发生每次需要遍历到链表的最后才 能获得合适的内存空间

复制算法
复制算法的核心思想是:
1.准备两块空间From空间和To空间,每次在对象分配阶段,只能使用其中一块空间(From空间)。
2.在垃圾回收GC阶段,将From中存活对象复制到To空间。
3.将两块空间的From和To名字互换

完整的复制算法的例子:
1.将堆内存分割成两块From空间To空间,对象分配阶段,创建对象。
2.GC阶段开始,将GC Root搬运到To空间
3.将GC Root关联的对象,搬运到To空间
4.清理From空间中未被标记的对象(未被引用的对象,还有弱引用),并把名称互换

优缺点

标记整理算法
标记整理算法也叫标记压缩算法,是对标记清理算法中容易产生内存碎片问题的一种解决方案。
核心思想分为两个阶段:
1.标记阶段,将所有存活的对象进行标记。Java中使用可达性分析算法,从GC Root开始通过引用链遍历出 所有存活对象。
2.整理阶段,将存活对象移动到堆的一端。清理掉存活对象之前占用的内存空间。
优缺点

分代垃圾回收算法
现代优秀的垃圾回收算法,会将上述描述的垃圾回收算法组合进行使用,其中应用最广的就是分代垃圾回收 算法(Generational GC)。
分代垃圾回收将整个内存区域划分为年轻代和老年代:





arthas查看分代之后的内存情况
在JDK8中,添加-XX:+UseSerialGC参数使用分代回收的垃圾回收器,运行程序
在arthas中使用memory命令查看内存,显示出三个区域的内存情况

调整内存区域的大小

垃圾回收器
本章属于入门教程,所以对于垃圾回收器的实战和原理会在进阶篇
为什么分代GC算法要把堆分成年轻代和老年代?
- 系统中的大部分对象,都是创建出来之后很快就不再使用可以被回收,比如用户获取订单数据,订单数据返回 给用户之后就可以释放了。
- 老年代中会存放长期存活的对象,比如Spring的大部分bean对象,在程序启动之后就不会被回收了。
- 在虚拟机的默认设置中,新生代大小要远小于老年代的大小。

分代GC算法将堆分成年轻代和老年代主要原因有:
1、可以通过调整年轻代和老年代的比例来适应不同类型的应用程序,提高内存的利用率和性能。
2、新生代和老年代使用不同的垃圾回收算法,新生代一般选择复制算法,老年代可以选择标记-清除和标记-整理 算法,由程序员来选择灵活度较高
3、分代的设计中允许只回收新生代(minor gc),如果能满足对象分配的要求就不需要对整个堆进行回收(full gc),STW时间就会减少

垃圾回收器的组合关系

笔记到此结束,其余内容可以查看这个PDF
黑马JVM/基础/04-基础篇-垃圾回收.pdf · Autism_Btkrsr/Blog_md_to_pdf - 码云 - 开源中国 (gitee.com)
下一章:
相关文章:
【JVM】JVM基础教程(四)
上一章:【JVM】JVM基础教程(三)-CSDN博客 目录 自动垃圾回收 方法区的回收 方法区回收条件 手动触发回收 堆回收 如何判断堆上的对象可以回收? 可以给对象引用赋值null,切断引用 引用计数法 循环引用缺点 查…...
深入了解Text2SQL开源项目(Chat2DB、SQL Chat 、Wren AI 、Vanna)
深入了解Text2SQL开源项目(Chat2DB、SQL Chat 、Wren AI 、Vanna) 前言1.Chat2DB2.SQL Chat3.Wren AI4.Vanna 前言 在数据驱动决策的时代,将自然语言查询转化为结构化查询语言(SQL)的能力变得日益重要。无论是小型创业…...
websocket 服务 pinia 全局配置
websocket 方法类 // stores/webSocketStore.ts import { defineStore } from "pinia";interface WebSocketStoreState {ws: WebSocket | null; // WebSocket 实例callbacks: ((message: string) > void)[]; // 消息回调函数列表connected: boolean; // 连接状态…...
基于Springboot企业oa管理系统【附源码】
基于Springboot企业oa管理系统 效果如下: 系统主页面 用户管理页面 公告信息管理页面 客户关系管理页面 车辆信息管理页面 工资信息管理页面 文件信息管理页面 上班考勤管理页面 研究背景 随着信息化时代的到来和企业OA管理理念的更新,企业面临着日益…...
Python遥感开发之地理探测器的实现
Python遥感开发之地理探测器的实现 1 地理探测器介绍2 官方软件实现3 Python代码实现 前言:本篇博客主要介绍使用py_geodetector库来实现地理探测器。 1 地理探测器介绍 官网链接:http://www.geodetector.cn/index.html 地理探测器用于测量和归因空间分…...
【HarmonyOS】 鸿蒙保存图片或视频到相册
【HarmonyOS】 鸿蒙保存图片或视频到相册 前言 鸿蒙中保存图片或者视频,或者其他媒体文件到设备的媒体库,可以是相册,也可以是文件管理等。共有两种方式: 需要应用申请受限权限,获取文件读写的权限(调用…...
Apache Echarts和POI
目录 Apache ECharts 介绍 入门 绘制一个简单的图表 Apache POI 介绍 通过POI创建Excel文件并且写入文件内容 通过POI读取Excel文件中的内容 导出Excel表格 Apache ECharts 介绍 Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观…...
厦门凯酷全科技有限公司正规吗靠谱吗?
随着短视频和直播电商的迅猛发展,越来越多的企业开始将目光投向抖音这一平台。作为国内领先的短视频社交平台,抖音凭借其庞大的用户基础和强大的算法推荐系统,成为众多品牌拓展市场、提升销售的重要渠道。厦门凯酷全科技有限公司(…...
WireShark 下载、安装和使用
1、下载 官网下载太慢,本人另外提供下载地址【下载WireShark】 2、安装 全部默认下一步即可,但如下图所示的这一步值得拿出来说一下。这一步是要你安装Npcap,但是你的电脑如果已经安装了WinPcap,那么可以选择不再安装Npcap。Npca…...
2025周易算命网站搭建详细方法+源码选择php环境的配置
以下是一个详细的搭建教程,包括网站分类、环境配置、程序设计和功能实现。 1. 环境准备 1.1 服务器选择 操作系统: Linux(推荐使用Ubuntu或CentOS)Web服务器: Nginx数据库: MySQLPHP版本: 7.4.x(确保小于8.0) 1.2 安…...
共享购模式革新登场:重构消费生态,领航商业新未来
近期,一种创新的商业模式——共享购,在电子商务领域掀起了一股热潮,不仅吸引了广大消费者的目光,也激发了商家和资本市场的浓厚兴趣。共享购模式凭借其独到的消费积分累积与转换体系,正在逐步重塑消费生态,…...
centos kafka单机离线安装kafka服务化kafka tool连接kafka
a.版本&环境 linux版本:centos7.6 kafka: kafka_2.12 zookeeper:zookeeper_3.6.3(之前已经安装:linux zookeeper安装并服务化-CSDN博客) java:1.8(之前已经安装) windows kafka tool: 2.1 b.kafka单机安装 1.切换目录 cd downloads/,利用rz命令࿰…...
QT JSON文件解析
参考博客 https://blog.csdn.net/cpp_learner/article/details/118421096 1 打开文件,读取全部内容 QFile file("../Json/js.json"); if (!file.open(QFile::ReadOnly | QFile::Text)) {qDebug() << "cant open error!";return; }// 读…...
[小白系列]GPU-nvidia-smi指令
nvidia-smi(NVIDIA System Management Interface)是一种命令行实用程序,用于监控和管理NVIDIA GPU(图形处理器)的状态和性能。它提供了一种简单而强大的方式来获取有关GPU的实时信息,并且可以用于诊断、…...
在SQL Server中使用hash join来提高表连接的性能
在SQL Server中使用hash join来提高表连接性能时,需要考虑数据集的大小、索引情况以及查询的具体需求。 在SQL Server中使用hash join来提高表连接性能的情况主要包括以下几种: • 两个表都没有合适的索引:Hash join通常适合当两个表都没有索…...
《Django 5 By Example》阅读笔记:p493-p520
《Django 5 By Example》学习第 17 天,p493-p520 总结,总计 28 页。 一、技术总结 1.internationalization(国际化) vs localization(本地化) (1)18n,L10n,g11n 以前总觉得这两个缩写好难记,今天仔细看了下维基百科…...
【开源】基于SpringBoot框架的网上订餐系统 (计算机毕业设计)+万字毕业论文 T018
系统合集跳转 源码获取链接 一、系统环境 运行环境: 最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。 IDE环境: Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以 tomcat环境: Tomcat 7.x,8.x,9.x版本均可 操作系统…...
数据湖治理最佳实践
如果没有最佳实践,存储可能会变得无法维护。自动化数据质量、生命周期和隐私功能可持续清理/移动数据湖中的数据。数据湖正成为企业从大数据中提取价值的一种日益可行的解决方案,对于早期采用者和新用户来说都是合理的下一步。在独立的逻辑区域中随时可用…...
基于php求职招聘系统设计
摘要 随着社会信息化时代的到来,如今人们社会的生活节奏普遍加快,人们对于工作效率的要求也越来越高,企业 举办招聘会耗时耗财,个人参加招聘会漫无目的寻找不到“方向”,网络搜索工作量目的性不强,信息量繁…...
ensp实验-vrrp多网关配置
一、交换机与路由的配置区别 1. 角色定义交换机: Master 或 Backup: 交换机通常作为 Master 或 Backup 设备参与 VRRP,负责在主设备故障时接替其工作。路由器: Master 或 Backup: 路由器同样可以作为 Master 或 Backup 设备…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)
注:文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件:STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...
虚幻基础:角色旋转
能帮到你的话,就给个赞吧 😘 文章目录 移动组件使用控制器所需旋转:组件 使用 控制器旋转将旋转朝向运动:组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转:必须移动才能旋转,不移动不旋转控制器…...
