Unity:内存管理、GC优化

2023-05-16

目录

一、GC简介

1. 堆内存分配和回收机制

2. 垃圾回收时的操作

3. 何时会触发垃圾回收?

4. GC操作带来的问题

二、 GC优化

1.降低GC影响的方法

2.减少内存垃圾的数量

3.造成不必要的堆内存分配的因素

《1》字符串

《2》容器类

《3》 匿名方法(Lambda)和闭包

《4》Unity函数调用

《5》装箱操作

《6》协程

《7》foreach循环

《8》函数引用

《9》 UGUI的重构

《10》语言集成查询LINQ和常量表达式

4.重构代码来减小GC的影响

5. 定期执行GC操作

三、检测堆内存分配:Unity profiler window


一、GC简介

在游戏运行的时候,数据主要存储在内存中,当游戏的数据在不需要的时候,存储当前数据的内存就可以被回收以再次使用。内存垃圾是指当前废弃数据所占用的内存,垃圾回收(GC)是指将废弃的内存重新回收再次使用的过程。

Unity中将垃圾回收当作内存管理的一部分,如果游戏中废弃数据占用内存较大,则游戏的性能会受到极大影响,此时垃圾回收会成为游戏性能的一大障碍点。

1. Unity内部有两个内存管理池:堆内存和栈内存

堆内存(heap) 主要用来存储较大的和存储时间较长的数据,主要是负责程序中的对象和数据。

栈内存(stack) 主要用来存储较小的和短暂的数据,主要是负责运行时的代码,例如函数调用。

2. 哪些数据在堆和栈上?

栈->值类型: bool byte char decimal double enum float int long sbyte short struct uint ulong ushort

堆->非空引用类型: class interface delegate object string,同时包含:
               1) 值类型数组
               2) 装箱的值类型

3. Unity托管堆简介

托管堆是由项目的脚本运行时(Scripting Runtime)——Mono或者IL2CPP内存管理器管理的一个内存片段:底层都是在C++分配内存。

        (1)   Unity Mono内存是只升不降,即使用后,哪怕空闲再多,也不会还给系统
        (2)   Il2cpp,则与一般的C++内存释放一样,释放的内存都会还给系统

4.Unity的GC机制

使用了Boehm GC算法(可以参考:https://en.wikipedia.org/wiki/Boehm_garbage_collector),是非分代(non-generational)和非压缩(non-compacting)的。

1) "非分代"是指GC执行清理操作时,必须遍历整个内存,去标记哪些没有被引用并且删除,随着内存的增长,它的性能就会降低。 目前2019版本的unity在实验分代GC算法

2) “非压缩”意味着内存中的对象不会被重新定位,去减小对象之间的内存空隙

1. 堆内存分配和回收机制

堆内存上的内存分配和存储相对而言更加复杂,主要是堆内存上可以存储短期较小的数据,也可以存储各种类型和大小的数据。其上的内存分配和回收顺序并不可控,可能会要求分配不同大小的内存单元来存储数据。

  堆上的变量在存储的时候,主要分为以下几步:

1)首先,unity检测是否有足够的闲置内存单元用来存储数据,如果有,则分配对应大小的内存单元;

2)如果没有足够的存储单元,unity会触发垃圾回收来释放不再被使用的堆内存。这步操作是一步缓慢的操作,如果垃圾回收后有足够大小的内存单元,则进行内存分配。

3)如果垃圾回收后并没有足够的内存单元,则unity会扩展堆内存的大小,这步操作会很缓慢,然后分配对应大小的内存单元给变量。

堆内存的分配有可能会变得十分缓慢,特别是在需要垃圾回收和堆内存需要扩展的情况下,通常需要减少这样的操作次数。

2. 垃圾回收时的操作

当堆内存上一个变量不再处于激活状态的时候,其所占用的内存并不会立刻被回收,不再使用的内存只会在GC的时候才会被回收。

  每次运行GC的时候,主要进行下面的操作:

1)GC会检查堆内存上的每个存储变量;

2)对每个变量会检测其引用是否处于激活状态;

3)如果变量的引用不再处于激活状态,则会被标记为可回收;

4)被标记的变量会被移除,其所占有的内存会被回收到堆内存上。

GC操作是一个极其耗费的操作,堆内存上的变量或者引用越多则其运行的操作会更多,耗费的时间越长。

注意:包含引用类型的结构体:struct是值类型的变量,但是如果struct中包含有引用类型的变量,那么GC就必须检测整个struct,大大增加GC工作量。

3. 何时会触发垃圾回收?

主要有三个操作会触发垃圾回收:

1) 在堆内存上进行内存分配操作而内存不够的时候都会触发垃圾回收来利用闲置的内存;

2) GC会自动的触发,不同平台运行频率不一样;

3) GC可以被强制执行。

特别是在堆内存上进行内存分配时内存单元不足够的时候,GC会被频繁触发,这就意味着频繁在堆内存上进行内存分配和回收会触发频繁的GC操作。

4. GC操作带来的问题

《1》.GC操作会需要大量的时间来运行,如果堆内存上有大量的变量或者引用需要检查,则检查的操作会十分缓慢,这就会使得游戏运行缓慢。

《2》GC可能会在关键时候运行,例如在CPU处于游戏的性能运行关键时刻,此时任何一个额外的操作都可能会带来极大的影响,使得游戏帧率下降。

《3》另外一个GC带来的问题是堆内存的碎片划。当一个内存单元从堆内存上分配出来,其大小取决于其存储的变量的大小。当该内存被回收到堆内存上的时候,有可能使得堆内存被分割成碎片化的单元。也就是说堆内存总体可以使用的内存单元较大,但是单独的内存单元较小,在下次内存分配的时候不能找到合适大小的存储单元,这也会触发GC操作或者堆内存扩展操作。

堆内存碎片会造成两个结果,一个是游戏占用的内存会越来越大,一个是GC会更加频繁地被触发。

二、 GC优化

1.降低GC影响的方法

《1》 减少GC的运行次数:减少托管堆内存的分配频率

《2》 减少单次GC运行的时间:涉及到对游戏的重构,减少变量和引用的分配,更少的引用会减少GC操作中的检测个数从而提高GC的运行效率

《3》 主动GC:将GC的运行时间延迟,避免在关键时候触发,比如可以在场景加载/切换的时候、关闭UI的时候lua调用GC等。

基于以上方法,可以采用三种策略:

(1) 减少堆内存的分配和引用的分配。更少的变量和引用会减少GC操作中的检测个数从而提高GC的运行效率。

(2) 降低堆内存分配和回收的频率,尤其是在关键时刻。也就是说更少的事件触发GC操作,同时也降低堆内存的碎片化。

(3) 可以试着测量GC和堆内存扩展的时间,使其按照可预测的顺序执行。这样操作的难度极大,但是这会大大降低GC的影响。

2.减少内存垃圾的数量

《1》缓存

如果在代码中反复调用某些造成堆内存分配的函数但是其返回结果并没有使用,这就会造成不必要的内存垃圾,我们可以缓存这些变量来重复利用。

《2》不要在频繁调用的函数中反复进行堆内存分配

在MonoBehaviour中,如果我们需要进行堆内存分配,最坏的情况就是在其反复调用的函数中进行堆内存分配,例如Update()和LateUpdate()函数这种每帧都调用的函数,这会造成大量的内存垃圾。我们可以考虑在Start()或者Awake()函数中进行内存分配,这样可以减少内存垃圾。或者在Update中采用计时器,特别是在运行有规律但是不需要每帧都运行的代码中。

《3》清除链表

在堆内存上进行链表的分配的时候,如果该链表需要多次反复的分配,我们可以采用链表的clear函数来清空链表从而替代反复多次的创建分配链表。

void Update()
{
    List myList = new List();
    PopulateList(myList);        
}
===》  
private List myList = new List();
void Update()
{
    myList.Clear();
    PopulateList(myList);
}

《4》对象池

即便我们在代码中尽可能地减少堆内存的分配行为,但是如果游戏有大量的对象需要产生和销毁依然会造成GC。对象池技术可以通过重复使用对象来降低堆内存的分配和回收频率。对象池在游戏中广泛的使用,特别是在游戏中需要频繁的创建和销毁相同的游戏对象的时候,例如枪的子弹这种会频繁生成和销毁的对象。

3.造成不必要的堆内存分配的因素

《1》字符串

字符串是不可变的引用类型变量,每次对字符串进行一些拆分和拼接操作时,都会产生新的字符串

减少字符串影响的方法:

(1) 减少不必要的字符串的创建,如果一个字符串被多次利用,我们可以创建并缓存该字符串。

(2) 减少不必要的字符串操作,例如如果在Text组件中,有一部分字符串需要经常改变,但是其他部分不会,则我们可以将其分为两个部分的组件,对于不变的部分就设置为类似常量字符串即可

(3) 字串符拼接采用 StringBuilder,尽量避免使用 + 操作符 和 strting.Format方法

,从而减少字符串产生的内存垃圾。

(4) 移除游戏中的Debug.Log()函数的代码,尽管该函数可能输出为空,对该函数的调用依然会执行,该函数会创建至少一个字符(空字符)的字符串。如果游戏中有大量的该函数的调用,这会造成内存垃圾的增加。

(5) 按时间刷新的字符串,例如:能按分钟更新,就不要按秒刷新字串符

(6) 使用string.Intern来减少字符串数量达到优化内存的效果,主要是针对固定不变的字符串,例如策划配置的文本数据

(7) 避免装箱,例如 newStr = "text" + 1 -> "text" + 1.ToString() 后者是采用非托管的方法直接操作内存完成

(8) 值类型转换字符串务必采用ToString()方法

《2》容器类

(1)  容器大小确定时,初始化时就直接设置目标大小,例如 项目中文本字符串字典初始化

(2)  List集合类

    1) 尽量重用,不要临时分配,例如:目前list类使用最频繁,尽量临时分配时采用 ListPool<T>.Get , 使用完再ListPool<T>.Release

    2) 查找list中对象时,避免直接使用Find, FindAll方法,建议采用for循环查找方式丢入ListPool中

    3) FindAll方法中注意千万不要有GC分配的操作

(3)  字典和枚举

    将枚举作为字段的Key,会引起装箱操作,有额外的临时内存分配,因为内部是采用Object.GetHashCode(Object) 来获取哈希值来作为Key值,只要是涉及到Key操作的都会引起装箱,例如Add,TryGetValue, ContainKey, Remove等等

《3》 匿名方法(Lambda)和闭包

(1) 在C#中所有方法的引用都是引用类型,都会被分配到堆中。把一个方法作为参数传递时,都会产生临时的内存分配,不管传递的是匿名方法还是已经定义的方法。

(2) 如果一个匿名函数引用到外部变量,则会形成一个闭包,C#为了实现这一点会生成一个匿名类(记住,类都是引用类型)来保存用到的外部变量,所以当调用这个闭包时,首先会实例化一个副本,同时会采用外部变量实际值来初始化这个副本,最终导致会在堆上分配内存。

优化方法

1) 尽量避免将方法作为参数传递,如果无法避免,优先采用匿名方法

2)尽量避免使用闭包,如果无法避免,绝对不能每帧执行的函数中使用闭包

《4》Unity函数调用

在代码编程中,当我们调用不是我们自己编写的代码,无论是Unity自带的还是插件中的,我们都可能会产生内存垃圾。Unity的某些函数调用会产生内存垃圾,我们在使用的时候需要注意它的使用。

这儿没有明确的列表指出哪些函数需要注意,每个函数在不同的情况下有不同的使用,所以最好仔细地分析游戏,定位内存垃圾的产生原因以及如何解决问题。有时候缓存是一种有效的办法,有时候尽量降低函数的调用频率是一种办法,有时候用其他函数来重构代码是一种办法。

在Unity中如果函数需要返回一个数组,则一个新的数组会被分配出来用作结果返回,这不容易被注意到,特别是如果该函数含有迭代器,下面的代码中对于每个迭代器都会产生一个新的数组:

void ExampleFunction()
{
    for(int i=0; i < myMesh.normals.Length;i++)
    {
        Vector3 normal = myMesh.normals[i];
    }
}
//对于这样的问题,我们可以缓存一个数组的引用,这样只需要分配一个数组
//就可以实现相同的功能,从而减少内存垃圾的产生: 
==》优化
void ExampleFunction()
{
    Vector3[] meshNormals = myMesh.normals;
    for(int i=0; i < meshNormals.Length;i++)
    {
        Vector3 normal = meshNormals[i];
    }
}

此外另外的一个函数调用GameObject.name 或者 GameObject.tag也会造成预想不到的堆内存分配,这两个函数都会将结果存为新的字符串返回,这就会造成不必要的内存垃圾,对结果进行缓存是一种有效的办法,但是在Unity中都对应的有相关的函数来替代。对于比较gameObject的tag,可以采用GameObject.CompareTag()来替代。

  在下面的代码中,调用gameobject.tag就会产生内存垃圾:

private string playerTag="Player";
void OnTriggerEnter(Collider other)
{
    bool isPlayer   = other.gameObject.tag == playerTag;
}
// 采用GameObject.CompareTag()可以避免内存垃圾的产生:
private string playerTag   = "Player";
void OnTriggerEnter(Collider other)
{
    bool isPlayer   = other.gameObject.CompareTag(playerTag);
}

不只是GameObject.CompareTag,unity中许多其他的函数也可以避免内存垃圾的生成。比如我们可以用Input.GetTouch()和Input.touchCount()来代替Input.touches,或者用Physics.SphereCastNonAlloc()来代替Physics.SphereCastAll()。

《5》装箱操作

装箱操作是指一个值类型变量被用作引用类型变量时候的内部变换过程,如果我们向带有对象类型参数的函数传入值类型,这就会触发装箱操作。比如String.Format()函数需要传入字符串和对象类型参数,如果传入字符串和int类型数据,就会触发装箱操作。如下面代码所示:

void ExampleFunction()
{
    int cost =   5;
    string displayString   = String.Format("Price:{0} gold",cost);
}

在Unity的装箱操作中,对于值类型会在堆内存上分配一个System.Object类型的引用来封装该值类型变量,其对应的缓存就会产生内存垃圾。装箱操作是非常普遍的一种产生内存垃圾的行为,即使代码中没有直接的对变量进行装箱操作,在插件或者其他的函数中也有可能会产生。最好的解决办法是尽可能的避免或者移除造成装箱操作的代码。

《6》协程

调用 StartCoroutine()会进行内存分配,产生少量的内存垃圾,因为unity会生成实体来管理协程。所以在游戏的关键时刻应该限制该函数的调用。基于此,任何在游戏关键时刻调用的协程都需要特别的注意,特别是包含延迟回调的协程。

yield在协程中不会产生堆内存分配,但是如果yield带有参数返回,则会造成不必要的内存垃圾,例如:

yield return 0;

由于需要返回0,引发了装箱操作,所以会产生内存垃圾。这种情况下,为了避免内存垃圾,我们可以这样返回:

yield return null;

另外一种对协程的错误使用是每次返回的时候都new同一个变量,例如:

while(!isComplete)
{
    yield return new WaitForSeconds(1f);
}

我们可以采用缓存来避免这样的内存垃圾产生:

WaitForSeconds delay   = new WaiForSeconds(1f);
while(!isComplete)
{
    yield return delay;
}

如果游戏中的协程产生了内存垃圾,我们可以考虑用其他的方式来替代协程。重构代码对于游戏而言十分复杂,但是对于协程而言我们也可以注意一些常见的操作,比如如果用协程来管理时间,最好在update函数中保持对时间的记录。如果用协程来控制游戏中事件的发生顺序,最好对于不同事件之间有一定的信息通信的方式。对于协程而言没有适合各种情况的方法,只有根据具体的代码来选择最好的解决办法。

《7》foreach循环

在unity5.5以前的版本中,在foreach的迭代中都会生成内存垃圾,主要来自于其后的装箱操作。每次在foreach迭代的时候,都会在堆内存上生产一个System.Object用来实现迭代循环操作。在unity5.5中解决了这个问题,比如,在unity5.5以前的版本中,用foreach实现循环:

void ExampleFunction(List   listOfInts)
{
    foreach(int currentInt in listOfInts)
    {
        DoSomething(currentInt);
    }
}

如果游戏工程不能升级到5.5以上,则可以用for或者while循环来解决这个问题,所以可以改为

void ExampleFunction(List   listOfInts)
{
    for(int i=0;   i < listOfInts.Count; i++)
    {
        int currentInt   = listOfInts[i];
        DoSomething(currentInt);
    }
}

《8》函数引用

函数的引用,无论是指向匿名函数还是显式函数,在unity中都是引用类型变量,这都会在堆内存上进行分配。匿名函数的调用完成后都会增加内存的使用和堆内存的分配。具体函数的引用和终止都取决于操作平台和编译器设置,但是如果想减少GC最好减少函数的引用。

返回数组的函数调用或访问器,以及返回字符串的调用都会产生临时内存分配,可以尝试其他替代方案 例如

1) Mesh.vertices Mesh.normals 等等,如果进行频繁操作,可以采用缓存数据的方式,而不是函数调用获取

2) GameObject.name,GameObject.tag 后者的比较可以采用GameObject.CompareTag()来替代

3) Input.touches 可以采用Input.GetTouch(),Input.touchCount替代

《9》 UGUI的重构

UGUI的重构会引起临时的内存分配,重点减少触发重构因素发生频率

重构因素(顶点数据变化):

1) 激活图形(Graphic)组件

2) Graphic组件的大小,父节点变化

3) 任何颜色相关修改,例如:Image color, shadow effectColor等等

4) Image类型和参数修改,例如:Simple->Filled,fillCenter变化, fillAmount变化,sprite,uvRect变化等等

5) RawImage类型和参数修改,例如:texture,uvRect 变化

6) Text类型的字符串内容,字体,字体大小和对齐方式等修改,例如:text 字符串内容变化,supportRichText,resizeTextForBestFit,resizeTextMinSize,alignment,fontSize, horizontalOverflow,verticalOverflow,fontStyle变化等等

7) 其他:

    shadow组件 useGraphicAlpha effectDistance,effectColor变化

  1. 删除不需要的顶点数据,例如,修改UGUI VertexHelper类
  2. 当前项目我们只保留:顶点坐标,UV坐标,顶点Color和索引数据
  3. 法线,切线,UV1-3数据使用不到,直接注释掉了对这些数据的填充代码

《10》语言集成查询LINQ和常量表达式

由于LINQ和常量表达式以装箱的方式实现,所以在使用的时候最好进行性能测试。

LINQ 并不是所有的平台都完整的支持,例如IOS,并不支持部分操作,项目中是禁止使用。

4.重构代码来减小GC的影响

即使我们减小了代码在堆内存上的分配操作,代码也会增加GC的工作量。最常见的增加GC工作量的方式是让其检查它不必检查的对象。

《1》struct是值类型的变量,但是如果struct中包含有引用类型的变量,那么GC就必须检测整个struct。如果这样的操作很多,那么GC的工作量就大大增加。

在下面的例子中struct包含一个string,那么整个struct都必须在GC中被检查:

public struct ItemData
{
    public string name;
    public int cost;
    public Vector3   position;
}
private ItemData[] itemData;

我们可以将该struct拆分为多个数组的形式,从而减小GC的工作量:

private string[] itemNames;
private int[] itemCosts;
private Vector3[] itemPositions;

《2》另外一种在代码中增加GC工作量的方式是保存不必要的Object引用,在进行GC操作的时候会对堆内存上的object引用进行检查,越少的引用就意味着越少的检查工作量。在下面的例子中,当前的对话框中包含一个对下一个对话框引用,这就使得GC的时候会去检查下一个对象框:

public class DialogData
{
     private DialogData   nextDialog;
     public DialogData   GetNextDialog()
     {
           return nextDialog;
     }
}

通过重构代码,我们可以返回下一个对话框实体的标记,而不是对话框实体本身,这样就没有多余的object引用,从而减少GC的工作量:

public class DialogData
{
    private int nextDialogID;
    public int GetNextDialogID()
    {
       return nextDialogID;
    }
}

当然这个例子本身并不重要,但是如果我们的游戏中包含大量的含有对其他Object引用的object,我们可以考虑通过重构代码来减少GC的工作量。

5. 定期执行GC操作

主动调用GC操作

如果我们知道堆内存在被分配后并没有被使用,我们希望可以主动地调用GC操作,或者在GC操作并不影响游戏体验的时候(例如场景切换的时候),我们可以主动的调用GC操作:

System.GC.Collect() //调用GC

通过主动的调用,我们可以主动驱使GC操作来回收堆内存。

三、检测堆内存分配:Unity profiler window

我们可以用unity->window->profiler窗口来检测堆内存分配以及一些其他的性能数据。

当选中某一帧的时候,下方会出现详细调用

 

参考文章链接:https://www.cnblogs.com/zblade/p/6445578.html

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity:内存管理、GC优化 的相关文章

  • 机器学习中数据不均衡问题(分类类别数据不均匀)

    在机器学习中 xff0c 我们经常会遇到类别数据分布不均衡问题 xff0c 即某类中含有很多数据 xff0c 而其他类别中的数据量很少 在这种情况下 使用传统机器学习算法开发的预测模型可能存在偏差和不准确 xff0c 造成上述的原因是 xf
  • EAST: An Efficient and Accurate Scene Text Detector翻译

    Abstract 用于场景文本检测的先前方法已经在各种基准测试中获得了良好的性能 然而 xff0c 在处理具有挑战性的场景时 xff0c 即使配备了深度神经网络模型 xff0c 它们通常也会达不到很好性能 xff0c 因为整体性能取决于管道
  • pytorch搭建网络结构

    记录pytorch怎么搭建网络 xff0c 看起来更舒服 首先定义一个block class myBlock nn module def init self in channel out channel xff1a super myBloc
  • ubuntu codename

    1 查看当前系统的codename lsb release a 2 历史版本codename Ubuntu 发布版本的官方名称是 Ubuntu X YY xff0c 其中 X 表示年份 xff08 减去2000 xff09 xff0c YY
  • xubuntu_18.04取消开机自动登录

    虽然开机自动登录听方便的 xff0c 但是总感觉不是那么的安全 xff0c 就把开机自动登录取消了 xff0c 下面是取消自动登录的方法 1 首先查一下自动登录的用户 grep R 34 autologin 34 etc lightdm 注
  • 双目相机与IMU联合标定

    前言 为了后面的视觉激光融合SLAM以及跑通VINS Fusion xff0c 需要标定双目相机和IMU得内参以及它们得外参 xff08 变换矩阵 xff09 准备工作 双目相机 xff1a ZED mIMU xff1a realsense
  • Python中画图时候的线类型

    在Python中用matplotlib画图的时候 xff0c 为了区分曲线的类型 xff0c 给曲线上面加一些标识或者颜色 以下是颜色和标识的汇总 颜色 xff08 color 简写为 c xff09 xff1a 蓝色 xff1a 39 b
  • Ubuntu20.04中,安装微信步骤总结

    先安装git xff08 如果已安装 xff0c 清忽略 xff09 sudo apt install git 检查git是否安装成功 xff08 如果已安装 xff0c 清忽略 xff09 git version 安装deepin win
  • touchgfx 浮点数显示

    最近在研究touchgfx xff0c 太懵了 xff0c 也就好久没有更新博文了 xff0c 很坑的一点 xff0c 我就想在屏幕上显示一个小数 xff0c 翻看了N多的博文 xff0c 没一个管用的 xff0c 这里分享一下我的方案 选
  • 浅谈专线(SD-WAN)

    存在就一定有存在的道理 xff0c 今天要说的网络知识点是专线 我们一步步来 xff0c 先说专线是什么 xff0c 然后说专线的特性 xff0c 最后说专线的未来 一 什么是专线 专线就是专门分的线路 xff0c 是运营商为企业客户分配的
  • Ubuntu18系统设置自定义分辨率1920*1080

    一般安装完unbuntu后会发现系统分辨率没有19201080 xff0c 需要手动自定义添加19201080分辨率 打开终端 xff0c 输入命令 xff1a xrandr xff0c 可以查看系统所有分辨率 输入命令生成显示 xff1a
  • 基于饿了么骨架屏方案,使用Chrome扩展程序生成网页骨架屏

    前言 之前写移动端项目的时候 xff0c 使用骨架屏来解决首屏渲染时出现短暂空白现象 xff0c 采用了就是饿了么page skeleton webpack plugin方法 但是page skeleton webpack plugin需要
  • 别瞎删package-lock.json了

    作者 xff1a wuwhs 原文 xff1a https segmentfault com a 1190000039684460 0 前言 看完本文 xff0c 你将从整体了解依赖版本锁定原理 xff0c package lock jso
  • Naive-UI,尤大推荐的Vue组件库

    前言 早上坐地铁上班的时候 xff0c 刷到推特推送了naive组件库有关的信息 点进去看了介绍 xff0c 觉得不错 xff0c 就来分享一下 组件库文档地址 xff1a www naiveui com 值得注意的是 xff0c vue作
  • 2021下半年最新前端求职面试指导(完整版)

    两周前 xff0c 学弟通过了阿里六轮面试 xff0c 顺利拿到了 Offer xff01 负责高德地图的前端部署 薪资待遇很不错 xff0c 30K 15薪 在此之前 xff0c 他在国营公司干了3 年 xff0c 这次回到大厂 xff0
  • 一文讲懂npm link

    前言 在本地开发npm模块的时候 xff0c 我们可以使用npm link命令 xff0c 将npm 模块链接到对应的运行项目中去 xff0c 方便地对模块进行调试和测试 用法 包链接是一个两步过程 xff1a 1 为依赖项创建全局软链np
  • Typescript中的extends关键字

    前言 extends关键字在TS编程中出现的频率挺高的 xff0c 而且不同场景下代表的含义不一样 xff0c 特此总结一下 xff1a 表示继承 拓展的含义表示约束的含义表示分配的含义 基本使用 extends是 ts 里一个很常见的关键
  • 记一次 React 开源甘特图组件的性能优化,已合入 PR!

    背景 公司项目最近用到甘特图功能 xff0c 于是集成了一款开源的甘特图插件 甘特图的主要作用是项目管理 xff0c 可以用图示的方式通过活动列表和时间刻度形象地表示出任何特定项目的活动顺序与持续时间 xff0c 如下图 image png
  • 【干货】Chrome插件(扩展)开发全攻略

    作者 xff1a 小茗同学 https www cnblogs com liuxianan p chrome plugin develop html comments 一 写在前面 我花了将近一个多月的时间断断续续写下这篇博文 xff0c
  • 很多人上来就删除的package-lock.json,还有这么多你不知道的(深度内容)

    作者 xff1a wuwhs 原文 xff1a https segmentfault com a 1190000039684460 0 前言 看完本文 xff0c 你将从整体了解依赖版本锁定原理 xff0c package lock jso

随机推荐

  • Svelte 原理浅析与评测

    简介 Svelte 是一个构建 web 应用程序的工具 xff0c 与 React 和 Vue 等 JavaScript 框架类似 xff0c 都怀揣着一颗让构建交互式用户界面变得更容易的心 但是有一个关键的区别 xff1a Svelte
  • CKS32F103C8T6最小系统板调试记录——SWD下载

    cks32f103是国产芯片 xff0c 由中科芯研发 xff0c 比起ST公司F103多了一个刹车功能 它有keil的器件安装包 xff0c 支持keil的开发 器件安装包链接 xff1a 链接 百度网盘链接 提取码 xff1a 0xyu
  • React useEvent:砖家说的没问题

    之前写了一篇文章 React Hooks 使用误区 xff0c 驳官方文档 1 xff0c 文中抛出了两个观点 xff1a 不是所有的依赖都必须放到依赖数组中deps 参数不能缓解闭包问题 这两个观点引起了剧烈的讨论 xff0c 当然大多数
  • Disabled PicPipeline: ImagesPipeline requires installing Pillow 4.0.0 or later

    目录 一 scrapy是什么 二 问题以及原因 三 解决办法 1 确保系统已经安装了 Pillow 库 2 安装 Pillow 库 3 在项目根目录中添加 Pillow 的 pth 文件 一 scrapy是什么 Scrapy是一个用于从网站
  • ERROR 1129 (HY000): Host ‘192.168.0.1‘ is blocked because of many connection errors; unblock with ‘m

    MySql远程链接报错 问题 xff1a mysql u root h 192 168 0 1 p Enter password ERROR 1129 HY000 Host 39 192 168 0 1 39 is blocked beca
  • JVM虚拟机详解

    一 JVM简介 JVM是Java Virtual Machine Java虚拟机 的缩写 简单来说JVM是用来解析和运行Java程序的 虚拟机是一种抽象化的计算机 通过在实际的计算机上仿真模拟各种计算机功能来实现的 Java虚拟机有自己完善
  • 云计算OpenStack详解

    一 OpenStack简介 1 OpenStack发展历程 2002年 美国著名的电商公司亚马逊 Amazon 干了一件 不务正业 的事 他们向客户推出了一项全新的业务 包括存储空间 计算能力等资源服务的Web nbsp Service 这
  • Linux虚拟化网络之vlan配置

    问题描述 Linux主机划分两个vlan 服务器server1的物理网卡的IP地址为1 1 1 1 24 服务器server2的物理网卡的IP地址为1 1 1 2 24 物理网卡下要虚拟化出来两个Vlan子接口 vlan10中主机的IP地址
  • 部署SDN控制器对接OVS网元实现转控分离实战 附ODL控制器

    1 云计算对网络的需求 1 多租户网络隔离 云中包含多个租户 不同租户可以自己设计自己的内部网络 172 16 0 0 10 0 0 192 168 0 存在安全隐患 要让不同租户网络要分隔开 nbsp 不同租户的网络需要互相二层隔离 三层
  • Python 数据采集、清洗、整理、分析以及可视化实战

    一 数据分析思路 大概可以分为下面这几个步骤 数据采集 原始数据完整性检查 数据清洗 整理 从不同角度对数据进行分析 数据可视化 总结 主要使用 Python 来进行分析 数据采集 主要涉及的 python 库包括 requests Bea
  • ERROR: torch-1.6.0+cu101-cp37-cp37m-win_amd64.whl is not a supported wheel on this platform.

    目的 xff1a 使用混合精度训练模型 方法 xff1a 最新版pytorch1 6已封装进混合量化的模块 xff0c 只需几句代码就可以提高batch size 速度会有非常大的提升 安装pytorch xff1a pip install
  • Clash开启系统代理System proxy后无效,无流量

    在注册表目录 xff1a HKEY LOCAL MACHINE SOFTWARE Policies Microsoft Windows CurrentVersion Internet Settings 设置ProxySettingsPerU
  • Proximal Policy Optimization(PPO)算法原理及实现!

    Proximal Policy Optimization PPO 算法原理及实现 xff01 这两天看了一下李宏毅老师的强化学习课程的前两讲 xff0c 主要介绍了Policy Gradient算法和Proximal Policy Opti
  • 树莓派4b ubuntu 20 设置阿里源

    前言 设置国内源其实很简单 xff0c 但是由于我是下载的 64位 操作系统 xff0c 并且树莓派是arm架构 xff0c 所以有一点不同 执行 lsb release a 查看发行版本 ubuntu 64 ubuntu etc netp
  • CAS登录流程

    访问 http localhost analysis 请求会被ngixn如下配置拦截 location analysis root D work dist index index html index htm try files uri u
  • 解决centos7 sshd端口修改后,重启服务器sshd无法连接的问题

    针对centos7的变化 xff0c 修改sshd端口需要修改 etc ssh sshd config selinux 防火墙 1 修改 etc ssh sshd config 执行命令 xff1a vi etc ssh sshd conf
  • osmWebWizard.py: error: typemap file “E:\SUMO\tools\data\typemap\osmPolyconvert.typ.xml“ not found

    在使用sumo的时候 xff0c 根据官方文档 xff08 OSMWebWizard SUMO Documentation dlr de xff09 显示 xff0c 在tools目录下使用python osmWebWizard py 来生
  • 内核与驱动_08_键盘驱动原理及代码

    文章目录 技术原理Windows中从击键到内核流程 键盘硬件原理键盘过滤的框架搭建应用设备扩展键盘过滤模块的动态卸载键盘过滤的请求处理通常的处理 PNP的处理读的处理读完成的处理从请求中打印出按键信息从缓冲区中获得KEYBOARD INPU
  • LCD 12864B V2.0的使用

    内置ST7920控制器和中文字库的LCD12864的使用 前言 大家好 xff0c 我是小灬贱 今天我给大家带来LCD12864的使用方法以及我的一些经验 文章里面如有不妥之处或者表达不清晰的地方还请各位多多指教 可以在文下评论或者私信我
  • Unity:内存管理、GC优化

    目录 一 GC简介 1 堆内存分配和回收机制 2 垃圾回收时的操作 3 何时会触发垃圾回收 xff1f 4 GC操作带来的问题 二 GC优化 1 降低GC影响的方法 2 减少内存垃圾的数量 3 造成不必要的堆内存分配的因素 1 字符串 2