第十二课 Unity 内存优化_内存工具篇(Memory)详解
内存(Memory)
unity 内存部分也是优化过程中非常重要的一个环节,也会影像渲染过程中的同步等待与带宽问题。因此内存的优化也可能会给我们渲染开销带来精简,今天我们先来了解unity中的内存与使用到的内存工具。
Unity中的内存
- 托管内存:主要是指使用托管堆或者垃圾收集器自动分配和管理的内存也包括脚本堆栈与虚拟机内存。
- C#非托管内存:可以在C#与Unity Collection名字空间和包结合使用,不使用垃圾收集器管理的内存部分。如果使用数据结构,不建议使用system下的collection的数据结构,而是要使用unity collection下的数据结构进行开发。
- Native 内存:Unity 用于运行引擎的C++内存。
性能分析工具
下面我们聊一聊unity引擎提供了哪儿些内存方面的工具
- Unity Profiler下的Memory标签:这里显示了unity当下内存使用的追踪状态,包括各类内存的分配和使用情况,以及当前unity下分配的对象与资源占用的内存情况。2021以后版本,profiler不再提供对象抓取快照功能了。而是使用memory profiler 直接抓取内存快照了。
- Memory Profiler:我们可以通过它来抓取内存快照,也可以对比两个内存快照下unity对象与资源的差异。通过Tree Map来查看内存分配的视图。通过Object and Allocation标签查看具体对象内存快照,并可以通过链接直接找到原工程中对应的资源和对象,通过fragmentation标签可以查看内存片段,unity2022以后 memory profiler变得更加简洁了,针对native内存甚至可以查看到具体是分配到哪儿个allocators中的。
- Memory Settings中的设置:大家现在可以在权衡时间和空间维度上的性能指标做更精准的设置了。
- UPR中的内存快照功能:主要是针对移动设备,当你使用UPR做性能调试时会经常用到,它可以脱离unity编辑器,在运行时抓取内存信息与对象分配信息,并可以做到多帧对比。具体操作请参阅UPR手册,后期我们可能会单独做写一个帖子,纯翻译供大家阅读。
- mac或者ios上我们可以选择xcode提供的instrument下的allocation工具。
- android上可以使用android相关的系统命令或者android studio profiler工具。
Profiler-Memory
Total Memory Breakdown(总内存分解)
- ManagedHeap:托管堆,重点监控对象,不要让它超过20MB,否则可能会有性能问题!
- Graphics & Graphics Driver:驱动程序在纹理、渲染目标、着色器和网格数据上使用的估计内存量。
- Audio:音效及声音文件,重点优化对象,播放时长较长的音乐文件需要进行压缩成.mp3或.ogg格式,时长较短的音效文件可以使用.wav 或.aiff格式。
- Video:视频系统的估计内存使用量。
- Other:显示Unity跟踪的本机内存,但不在特定计数器下报告。
- Profiler:探查器功能从系统中使用和保留的内存。
Objects Status(对象状态,显示通常占用大量内存的资源类型(纹理,网格,材质,动画剪辑)的对象实例数量,以及它们在内存中的累积大小(资源,游戏对象,场景对象))
-
Texture2D: 2D贴图及纹理。重点优化对象,有以下几点可以优化:
1.许多贴图采用的Format格式是ARGB 32 bit所以保真度很高但占用的内存也很大。在不失真的前提下,适当压缩贴图,使用ARGB 16 bit就会减少一倍,如果继续Android采用RGBA Compressed ETC2 8 bits(iOS采用RGBA Compressed PVRTC 4 bits),又可以再减少一倍。把不需要透贴但有alpha通道的贴图,全都转换格式Android:RGB Compressed ETC 4 bits,iOS:RGB Compressed PVRTC 4 bits。
2.当加载一个新的Prefab或贴图,不及时回收,它就会永驻在内存中,就算切换场景也不会销毁。应该确定物体不再使用或长时间不使用就先把物体制空(null),然后调用Resources.UnloadUnusedAssets(),才能真正释放内存。
3.有大量空白的图集贴图,可以用TexturePacker等工具进行优化或考虑合并到其他图集中。
4.存在空白的或者纯色的贴图,可以用颜色节点代替
- Mesh:场景中使用的网格模型,注意网格模型的点面数,能合并的mesh尽量合并。
- Materials:加载的材质和它们使用的内存的总数。
- AnimationClip: 加载的动画和它们使用的内存的总数。
Assets(已加载资产的总数)
- Game Objects:游戏对象总数
- Scene Objects:这个数字包括游戏对象的数量,加上组件的总数,以及场景中所有不属于资产的东西。
- GC allocated in frame:显示选定帧中托管分配的数量及其总大小(以字节为单位)。
Memory Profiler
主要用来查看托管内存和本机内存的详细分配情况。它通过捕获、检查、比对内存快照的方式来检测内存泄漏和内存碎片。本篇文章中使用的版本是0.7.1版本。
安装
add PackageManager- >Add By Name- >输入com.unity.memoryprofiler
查看
Windows - > Analysis - > Memory Profiler
Memory Profiler界面,可以链接真机检测,也可以在Editor检测。
点击Capture New Snapshot截取保存当下帧的内容

点击上图中3号的位置Snap来查看详细的内容

通过观察,我们能看到上图的数据与Profiler中的Memory的数据是一致的

单帧检测
一般去看工程内的资源, 去检查占用内存特别大的游戏对象。
Memory Breakdowns界面可以查看unity内的具体游戏对象,也同样可以进行筛选

TreeMap界面进行检查, 这里已经分好类, 同时可以根据Size的大小进行排序,查看内存占用较大的游戏对象进行优化处理。

Fragmentation 页签进行查看, 点击对应的地址块,下方可显示详细信息。
1.该视图会将内存数据可视化成虚拟内存布局。如下图所示:
2.每一行都会显示一个内存块和起始地址标签。当起始地址标签上面有黑色背景时,就表明该起始地址就是内存块的开始部分,并且与之前的内存块之间存在不连续性;否则,就表明该起始地址就是内存块的一部分。如下图所示:

3.既可以通过单击起始地址标签来选择关联的内存块;也可以通过单击鼠标拖动的方式来选择感兴趣的内存块;甚至可以通过单击内存块中的虚拟内存来选择该内存块。
4.当选择内存块时,就会在Filters面板中将相关的虚拟内存按照指定的列表类型(区域列表-Regions list、分配列表-Allocations list、对象列表-Objects list)进行展示详细信息。
区域列表类型展示信息如下图所示:

分配列表类型展示信息如下图所示:

还有对象列表类型展示信息,操作同上。
Objects and Allocations页面可查看详细的对比内容,可以进行筛选
筛选方式:Select Table View
筛选之后就可以进行详细分析了,可以通过Type,Size, Referenced By等标签查看对应的游戏对象。
也可以鼠标右键点击下图1或者2来对类型和名字进行具体筛选。

两帧对比检测
一般使用两帧率对比用于检测内存泄漏。
在要对比的节点分别进行Capture New Snapshot截取, 点击Compare Snapshots进行对比,在分别点击两个Snap,进行对比。
Summary页签可看汇总的对比内容:

Objects and Allocations页面可查看详细的对比内容,可以进行筛选
筛选方式:Select Table View来查看以下几种类型数据:
- [Diff] Raw Data:从原始数据列表中选择一项原始数据(Root Reference、Native Allocation、Native Object等)进行查看。
- [Diff] All Managed Objects:查看所有的托管对象(IL2CPP、Mono)。
- [Diff] All Native Objects:查看所有继承自Unity.Object类型的本机对象。
- [Diff] All Objects:查看所有本机对象和托管对象。
- [Diff] Alloc:从分配列表中选择一项分配数据(ByNativeObject、ByRoot、ByMemRegion)进行查看。
筛选之后就可以进行详细分析了,可以通过Type,Size, Referenced By等标签查看对应的游戏对象。
也可以鼠标右键来对类型和名字进行具体筛选。

总结
MemoryProfiler 是一个非常好用的检查内存问题的工具,以下问题都可以通过该工具进行排查
- 查找有问题的游戏资源,例如:Mesh和贴图非常大的美术资源
- 内存泄漏问题
检测内存占用:可以使用Unity Memory Profiler来检测托管内存和本机内存的占用情况。检测流程如下所示:
- 首先打开Unity Memory Profiler窗口;然后打开想要检查的内存快照;最后在主视图区域以树形视图的方式来显示内存快照中深度内存数据。
- 查看树形视图中不同的对象类别。
- 单击树形视图中某一个对象类别,此时会展开该对象类别中所有的对象以及在主视图区域下方以对象表格的方式来显示该对象类别中所有的对象。
- 单击对象类别中某一个对象或者单击对象表格中某一个对象,进而可以在对象表格中查看该对象的具体信息。
- 首先将对象表格中所有的对象按照从高到低的顺序进行排序;然后优先从纹理、着色器变体、预分配缓冲区这三种对象来制定好减少内存的目标。
检测内存泄漏:可以使用Unity Memory Profiler来检测托管内存和本机内存的泄漏情况。如下所示:
1.出现内存泄漏的危害如下所示:
- 应用程序可能因为GC遍历对象时间变长的原因而出现卡顿现象。
- 应用程序可能因为可用内存空间不足的原因而出现闪退现象。
2.出现内存泄漏的原因如下所示:
- 对于自动垃圾回收而言,对象的引用计数不为0。
- 对于被动垃圾回收而言,对象没有被代码手动释放。
3.查找并修复场景卸载后发生的内存泄漏:流程如下所示:
- 使用Unity Memory Profiler来设置捕获目标。
- 首先在捕获目标上加载一个空场景;然后在该场景上拍摄一张内存快照。
- 首先在捕获目标上加载一个要检测内存泄漏的场景;然后在该场景上执行业务模块;最后将该场景卸载(调用Resources.UnloadUnusedAssets函数)掉或者切换到一个空场景。
- 在捕获目标上再拍摄一张内存快照。
- 为了避免处理内存快照文件和捕获目标之间竞争系统资源,建议此时关闭掉捕获目标。
- 首先在工作台区域打开第一张和第二张内存快照文件;然后单击Diff按钮来对两个打开的内存快照进行差异比对;最后将差异比对生成的数据显示在主视图区域中。
- 首先在主视图区域中选择Diff表格属性;然后选择Group排序规则来将相同值(Deleted、New、Same)的对象合并在一个组内;最后查看数值为New的分组,如果存在对象是在第二张内存快照中的话,就表明该对象的内存泄漏了。
4.查找并修复小的连续分配可能造成的内存泄漏:流程如下所示:
- 使用Unity Memory Profiler来设置捕获目标。
- 首先在捕获目标上加载一个要检测内存泄漏的场景;然后在该场景上拍摄第一张内存快照。
- 首先播放要检测内存泄漏的场景;接着在该场景上拍摄第二张内存快照;然后继续播放该场景;最后在该场景上拍摄第三张内存快照。
- 为了避免处理内存快照文件和捕获目标之间竞争系统资源,建议此时关闭掉捕获目标。
- 首先在工作台区域打开拍摄的第二张和第三张内存快照文件;然后单击Diff按钮来对两个打开的内存快照进行差异比对;最后将差异比对生成的数据显示在主视图区域中。
- 首先在主视图区域中选择Diff表格属性;然后选择Group排序规则来将相同值(Deleted、New、Same)的对象合并在一个组内。
- 首先在主视图中选择Owned Size表格属性;然后选择Group和Sort Descending排序规则来将相同值的对象合并在一个组内,并按照从大到小的顺序来排列组。
- 查看较大内存分配组中的对象是否同时存在于Same组和New组中,记录好满足条件的对象。
- 首先在工作台区域打开拍摄的第一张和第二张内存快照文件;接着单击Diff按钮来对两个打开的内存快照进行差异比对;然后将差异比对生成的数据显示在主视图区域中;最后执行4.6 ~ 4.8步骤,进而了解系统内潜在的内存泄漏。
元数据:如下所示:
1.元数据类型为MetaData,包含的字段如下所示:
- content:包含项目名称和捕获目标为Unity Editor时的脚本版本。
- platform:应用程序对应的目标平台。
- screenshot:针对捕获目标截取的屏幕截图(像素大小小于480x240)。
2.首先在捕获目标上拍摄内存快照时就会生成元数据;然后该元数据会自动添加到内存快照中;最后开发人员可以通过元数据来更好地了解内存快照的内容。
3.拍摄内存快照的方式如下所示:
- 当项目中有安装Unity Memory Profiler时,此时就可以在工具栏区域中点击Capture控件来针对捕获目标来拍摄一张内存快照。
- 在代码中通过MemoryProfiler.TakeSnapshot/TakeTempSnapshot函数来针对捕获目标拍摄一张内存快照。在调用该函数时,可以设置包含内存快照文件路径字符串和是否拍摄成功布尔值两个参数的结束回调函数。
4.生成元数据的方式如下所示:
- 当项目中没有安装Unity Memory Profiler时,此时可以首先给MemoryProfiler.createMetaData委托注册一个监听函数;然后在该监听函数中设置元数据。
- 当项目中有安装Unity Memory Profiler时,此时就会生成默认的元数据。
- 当项目中有安装Unity Memory Profiler时,此时就可以首先创建一个继承自MetadataCollect类型的元数据收集类型;然后在该类型里面重写CollectMetadata函数;最后在该函数中设置元数据。
项目中可能遇到的问题
首先要明确一点,在Editor中运行时,“Unity”大是正常的,因为在Editor中运行项目时,引擎包含了所有的资源占用的内存(除了部分纹理和Mesh是在GFX中),同时自身会进行很多的辅助操作来记录各种游戏运行信息。一般来说,在查看游戏运行时的真实消耗内存,我们均是推荐直接在发布游戏上通过Profiler进行查看,在Editor中运行游戏所看到的内存是要大很多的。
1.Device.Present:
- GPU的presentdevice确实非常耗时,一般出现在使用了非常复杂的shader.
- GPU运行的非常快,而由于Vsync的原因,使得它需要等待较长的时间.
- 同样是Vsync的原因,但其他线程非常耗时,所以导致该等待时间很长,比如:过量AssetBundle加载时容易出现该问题.
- Shader.CreateGPUProgram:Shader在runtime阶段(非预加载)会出现卡顿(华为K3V2芯片).
- StackTraceUtility.PostprocessStacktrace()和StackTraceUtility.ExtractStackTrace(): 一般是由Debug.Log或类似API造成,游戏发布后需将Debug API进行屏蔽。
2.Overhead:
- 一般情况为Vsync所致.
- 通常出现在Android设备上.
3.GC.Collect:
原因:
- 代码分配内存过量(恶性的)
- 一定时间间隔由系统调用(良性的).
占用时间:
- 与现有Garbage size相关
- 与剩余内存使用颗粒相关(比如场景物件过多,利用率低的情况下,GC释放后需要做内存重排)
4.GarbageCollectAssetsProfile:
- 引擎在执行UnloadUnusedAssets操作(该操作是比较耗时的,建议在切场景的时候进行)。
- 尽可能地避免使用Unity内建GUI,避免GUI.Repaint过渡GCAllow.
- if(other.tag == a.tag)改为other.CompareTag(a.tag).因为other.tag为产生180B的GC Allow.
- 少用foreach,因为每次foreach为产生一个enumerator(约16B的内存分配),尽量改为for.
- Lambda表达式,使用不当会产生内存泄漏.
5.尽量少用LINQ:
- 部分功能无法在某些平台使用.
- 会分配大量GC Allow.
6.控制StartCoroutine的次数:
- 开启一个Coroutine(协程),至少分配37B的内存.
- Coroutine类的实例 -> 21B.
- Enumerator -> 16B.
7.使用StringBuilder替代字符串直接连接.
8.缓存组件:
- 每次GetComponent均会分配一定的GC Allow.
- 每次Object.name都会分配39B的堆内存.
9.ManagedHeap.UsedSize是项目逻辑代码在运行时申请的堆内存,该选项只能通过优化代码来进行降低。 优化方法一般如下:
- 尽可能地复用变量,减少new的次数;
- 使用StringBuilder代替String连接,使用for代替foreach;
- 对于局部变量或非常驻变量,尽可能使用Struct来代替Class。
ManagedHeap.UsedSize过大,一方面可能会影响一次GC的耗时;另一方面也可能反映出脚本中不合理的GC Alloc。
10.有些小伙伴会发现System.ExecutableAndDlls占内存巨大,且一直在增长,是怎么回事?
System.ExecutableAndDlls该项显示的是执行文件和所调用的库(物理、渲染、IO等系统库)的总和。开发团队不用太担心该选项的数值,因为很多应用均在共用这些库,并且它对于真实项目的内存压力非常小,几乎没有影响,而且OS也不会因为该内存而杀掉游戏或应用。
11.凡是在Unity Profiler中能看到的资源就会保留在内存中。对于这种资源,在切换场景时调一下UnloadUnusedAssets API就可以释放。
12.Profiler.BeginSample统计到的数据与直接看Memory下的不一样,前者比后者的数据更大,这怎么理解?
这种情况确实也是经常会遇到的。一帧中分配如此高的内存是会触发GC.Collect的,而Mono中显示的数值则是GC之后的Mono内存数值。
13.正常情况下游戏如果一直玩下去,Mono是不是会一直增加? 比如频繁打开一个界面,界面里有脚本会不断创建一些东西 ,那么Mono是否会不断增加?对性能上会不会造成影响呢?
在除开启IL2CPP功能的应用中,Mono 确实是不会下降,但并不应该一直上升。
创建出来的东西,如果被引用在一个容器里,或者被某些脚本的变量引用,那么这部分堆内存就释放不掉;但如果没有被任何容器或者变量引用(比如,临时拼一个 String),那么这部分堆内存会在 GC 的时候释放(释放是指变为空闲的堆内存,堆内存的总量是不会下降的)。
对于后者,频繁地 new 对象虽然不会一直增加堆内存,但是会加速 GC 调用的频率,所以同样是需要尽量避免的。
14:我想请教一下,下图这个函数中,每次我都申请了一个List temp = list();在这里存放6KB的数据,但是如果不做GC处理,这6KB是否就一直累加,直到做GC处理了才会释放掉,是这样么?如果调用次数很多,每次都调用一点点,也会推高内存占用吗?
是的,这个6KB堆内存会随着Update的执行一直分配内存,所累积的堆内存会在GC触发时进行销毁。一般来说,研发团队需要尽可能避免在高频次调用函数中进行堆内存的分配。
15:在进行内存优化时,Unity Profiler给出的数据和Android系统(adb dumpsys meminfo,已经考虑memtrack的影响 )的数据差距较大(已经分析了Profiler自身的内存占用),如何分析这部分差异,比如包括对显存消耗进行准确统计,OS消耗的统计等等?
内存差异较大是正常的,一般来说,Profiler统计的内存较为一致,而Android系统通过ADB反馈的PSS、Private Dirty等值则是差别很大。这主要是因为芯片和OS的不同而导致。具体的Android内存,建议直接查看Google Android OS的相关文档。
Unity Profiler反馈的则是引擎的真实物理使用内存,一般我们都建议通过Profiler来查看内存是否存在冗余、泄露等问题。
16:已经预加载怪物,然后显示怪物 PSS上升,并且在隐藏怪物后并没有下降,这是什么原因导致?显存上去了吗?
仅仅隐藏怪物的话,内存是不会下降的。因为隐藏只是改变了GameObject的状态,并没有对内存中的Object和资源进行移除。同时,即使是提前加载了怪物,也依然可能存在以上问题,因为某些资源是在显示的时候,才会传输一份到GPU的,比如Mesh。一般情况下,显存都不会即刻降低,这个是由Graphics Driver来管理的。建议可以看Profiler是否增长,如果Profiler没有问题而PSS持续增长,就有可能发生了内存泄露。
对于这个问题,建议查看《性能优化,进无止境---内存篇(下)》加深理解。
17:对于Handheld.PlayFullScreenMovie 这个Unity播放开场动画的API,会有内存问题吗?比如我的mp4动画有20MB,那么这个动画会撑高mono堆内存吗?
Android上PlayFullScreenMovie 的实现实际上是通过Android原生的接口直接播放的,播放过程中Unity也是停止更新的,因此这部分的内存理论上并不会记录在 Unity 中,同样也不影响Mono。
18:Texture占用内存总是双倍,这个是我们自己的问题,还是Unity引擎的机制?
出现这种情况的原因有两种:一种是你在真机运行时开启了Read&Write。另一种可能是Unity的Bug,目前的Unity 5.2.3 release note如下 :
(735644) - OpenGL: Fixed texture memory usage reporting in profiler, was twice the actual size for most textures.
开发者需要关注下自己的开发版本,5.2.3以前类似情况的项目可以参考一下。
19:如果脚本引用了GameObject,那转换场景的时候脚本和GameObject都没了,还会产生堆内存的吗?
如果脚本是MonoBehaviour,而且在切换场景后所挂的Game Object被释放了,那么这个脚本对象所引用的堆内存就会在GC的时候被释放。 但有一种例外,如果是通过Static变量引用的堆内存,那么依然是释放不掉的,除非手动解开引用,比如变量置Null,数组Clear等等。
移动平台内存经验数据参考
Textures:80M-160M
Mesh:50M-70M
Render Textures:50M-80M
AnimationClips:30M-60M
Audio:10M-20M
Cubemap:0-50M
Font:5M-15M
Shader:20M-40M
System.xxx总和:15M-30M
AssetBundle:0-10M
其他各类对象单项:0-10M ,数量小于10000
ReservedMono:<100M
ReservedGFX:<300M
ReservedTotal:<650M
这些指标的上下限分别代表了在移动设备上的高低配数据的差异,其中Render Texture会根据目标设备的分辨率的不同会有差异变化。这里给出的是1080P分辨率下的经验数据指标。一些下限为零的指标为不使用此功能,可能没有这方面的开销数据,如果各个指标都在上述范围内,不优化也没有问题。
移动平台其他经验数据参考
DrawCall:300-600
SetPassCall:80-120
Triangles Count:60W-100W
Material Count:200-400
建议你的游戏相关指标也控制在此范围内,当然数据仅供参考。
在我的文章里你可能会看到重复的内容,原因是我的文章很多都是各路大神的心得,会有重复的,我没有删除,我觉得重复的多代表重要。
今天是2024年12月16日
重复一段毒鸡汤来勉励我和你
你的对手在看书
你的仇人在磨刀
你的闺蜜在减肥
隔壁的老王在练腰
而你在干嘛?
相关文章:
第十二课 Unity 内存优化_内存工具篇(Memory)详解
内存(Memory) unity 内存部分也是优化过程中非常重要的一个环节,也会影像渲染过程中的同步等待与带宽问题。因此内存的优化也可能会给我们渲染开销带来精简,今天我们先来了解unity中的内存与使用到的内存工具。 Unity中的内存 托…...
达梦8-达梦数据的示例用户和表
1、示例库说明: 创建达梦数据的示例用户和表,导入测试数据。 在完成达梦数据库的安装之后,在/opt/dmdbms/samples/instance_script目录下有用于创建示例用户的SQL文件。samples目录前的路径根据实际安装情况进行修改,本文将达梦…...
数据可视化-1. 折线图
目录 1. 折线图适用场景分析 1. 1 时间序列数据展示 1.2 趋势分析 1.3 多变量比较 1.4 数据异常检测 1.5 简洁易读的数据可视化 1.6 特定领域的应用 2. 折线图局限性 3. 折线图代码实现 3.1 Python 源代码 3.2 折线图效果(网页显示) 1. 折线图…...
【现代服务端架构】传统服务器 对比 Serverless
在现代开发中,选择合适的架构是至关重要的。两种非常常见的架构模式分别是 传统服务器架构 和 Serverless。它们各有优缺点,适合不同的应用场景。今天,我就带大家一起对比这两种架构,看看它们的差异,并且帮助你选择最适…...
论文学习—VAE
VAE----Auto-Encoding Variational Bayes 2024年12月17日-2024年12月18日摘要引言方法例子:变分自动编码器 2024年12月17日-2024年12月18日 从今天开始,我准备记录自己学习的内容以此来检验我每天的学习量,菜鸡一枚,希望能够与大…...
AI 智能体(AI Agent)到底什么原理?能干什么事情
智能体应用有哪些? 智能体在千行百业中有着广泛的应用,目前已经在 600 多个项目落地和探索,广泛应用于政府与公共事业、交通、工业、能源、金融、医疗、科研等行业。智能体是模拟人类智能的计算机系统,能自主感知环境、智能决策并…...
【mysql】如何查看大表记录行数
目录 1. 使用 ANALYZE TABLE 和 SHOW TABLE STATUS2. 查询 INFORMATION_SCHEMA 表3. 使用索引统计信息4. 维护行数缓存5. 使用分区计数 1. 使用 ANALYZE TABLE 和 SHOW TABLE STATUS 1.ANALYZE TABLE 可以更新表的统计信息,然后使用 SHOW TABLE STATUS 来查看估算的…...
Linux之网络配置
一、检查虚拟机和本机通不通 测试虚拟机和本机是否通不通 winR,运行本机cmd,输入ipconfig,拿到本机ip地址 在虚拟机上ping一下这个地址(ctrlshitv)可以把复制的文本粘贴进虚拟机。 可以看到,不通,解决方法在最后&am…...
SpringBoot集成JWT和Redis实现鉴权登录功能
目前市面上有许多鉴权框架,鉴权原理大同小异,本文简单介绍下利用JWT和Redis实现鉴权功能,算是抛砖引玉吧。 主要原理就是“令牌主动失效机制”,主要包括以下4个步骤: (1)利用拦截器LoginInterceptor实现所有接口登录拦…...
LabVIEW热电偶传感器虚拟仿真实验系统
在教学和科研领域,实验设备的更新和维护成本较高,尤其是在经济欠发达地区,设备的短缺和陈旧化严重影响了教学质量。基于LabVIEW的热电偶传感器虚拟仿真实验系统能够通过模拟实验环境,提供一个成本低廉且效果良好的教学和研究平台。…...
Centos7 部署ZLMediakit
1、拉取代码 #国内用户推荐从同步镜像网站gitee下载 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit cd ZLMediaKit #千万不要忘记执行这句命令 git submodule update --init 2、安装编译器 sudo yum -y install gcc 3、安装cmake sudo yum -y install cmake 4…...
Docker搭建kafka环境
系统:MacOS Sonoma 14.1 Docker版本:Docker version 27.3.1, build ce12230 Docker desktop版本:Docker Desktop 4.36.0 (175267) 1.拉取镜像 先打开Docker Desktop,然后在终端执行命令 docker pull lensesio/fast-data-dev …...
wsl2-ubuntu安装docker后无法拉取镜像
如上是报错全部信息, 这个实际上是因为网络不通导致的, 由于我实在公司使用, 而公司上网需要使用代理, 因此把代理加上就行了. # 为docker服务添加代理 mkdir /etc/systemd/system/docker.service.d cat > /etc/systemd/system/docker.service.d/http-proxy.conf <<…...
Invalid bound statement (not found) 错误解决
出现这个错误提示:Invalid bound statement (not found): com.xxx.small_reservior.dao.WaterRainMapper.getWaterRainByRegion,通常表示 MyBatis 框架无法找到与给定的 getWaterRainByRegion 方法匹配的 SQL 映射语句。这种问题通常发生在以下几种情况中…...
深度学习的下一站:解锁人工智能的新边界
引言:新边界的呼唤 深度学习的诞生,犹如人工智能领域的一次革命,激发了语音助手、自动驾驶、智能医疗等前沿技术的飞速发展。然而,面对现实世界的复杂性,现有的深度学习模型仍然存在数据依赖、可解释性差、环境适应力不…...
搭建Tomcat(三)---重写service方法
目录 引入 一、在Java中创建一个新的空项目(初步搭建) 问题: 要求在tomcat软件包下的MyTomcat类中编写main文件,实现在MyTomcat中扫描myweb软件包中的所有Java文件,并返回“WebServlet(url"myFirst")”中…...
跟着AI 学AI开发二,本地部署自己的Chat GPT
这里要安装的是Open Web UI ,用一张架构图说明AI 前端与后端的关系。 之前的Python 的方法已经做过多次介绍,这里不做赘述。 顺序:1,Ollama。 2,Docker。 3,Open WebUI。 Ollama 安装下载地址࿱…...
XXE靶机漏洞复现通关
1.扫描XXE靶机的ip地址 将kali虚拟机和XXE靶机部署在同一局域网中,都采用NAT网络模式 搭建好后在kali终端中进行扫描XXE靶机的ip arp-scan -l 根据常识我们可以推断192.168.27.153为靶机的ip地址 2.访问靶机页面并扫描附录 进入页面后我们可以打开御剑扫描网页中…...
XS9922B 同轴RX芯片 四通道 多合一模拟高清解码器
XS9922B 是一款 4 通道模拟复合视频解码芯片,支持 HDCCTV 高清协议和 CVBS 标 清协议,视频制式支持 720P/1080P 高清制式和 960H/D1 标清制式。芯片将接收到的高清 模拟复合视频信号经过模数转化,视频解码以及 2D 图像处理之后,转…...
如何在谷歌浏览器中设置电子邮件通知
在现代互联网生活中,电子邮件已成为我们日常沟通的重要工具。为了更高效地管理邮件,您可以在谷歌浏览器中设置电子邮件通知。本文将详细介绍如何实现这一功能,并附带一些相关的Chrome使用技巧。(本文由https://chrome.google64.cn…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor
1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

