【前端知识】内存泄漏与垃圾回收机制 (下)

news2024/11/25 2:22:34

【前端知识相关分享】内存泄漏与垃圾回收机制 (下)

  • 6. 内存泄漏的解决方法
    • 6.1 解决方法概述
    • 6.2 什么是垃圾
    • 6.3 垃圾回收机制的定义及规则
    • 6.4 垃圾回收算法的基本流程
  • 7. 垃圾回收的常见算法
    • 7.1 引用计数
    • 7.2 标记清除
    • 7.3 复制算法
    • 7.4 标记整理(标记压缩)
    • 7.5 四种基本GC算法的总结
  • 8. 垃圾回收机制的优化
    • 8.1 分代回收
    • 8.2 增量回收
    • 8.3 惰性清理(空闲时间收集)
    • 8.4 并行回收
  • 8.5 并发回收
  • 9. V8的垃圾回收策略
    • 9.1 V8新生代的垃圾回收
    • 9.2 V8老生代的垃圾回收
    • 9.3 总结
  • 10. 内存管理的性能优化
  • 相关博客推荐

五一假期第2天,承接上篇的知识笔记。
本文为原创,未经同意请勿转载

由于篇幅有点长,所以笔者将我关于这部分的笔记分为上下两个篇章(文章开头有附录上篇链接),避免读者的阅读疲倦感😵,同时也方便大家的阅读啦🤗。下面的笔记笔者也结合了自己所学的东西和平常使用的情况进行了相关的拓展。如果下面笔记中存在错误,欢迎大家及时指正,学习与讨论。

上章:介绍内存的生命周期,管理及内存泄漏的相关概念,分析内存泄漏可能的原因。
上篇博客的链接:【前端知识】内存泄漏与垃圾回收机制 (上)
下章:内存泄漏的解决方法,垃圾回收机制的相关知识,内存管理的优化等。

6. 内存泄漏的解决方法

对于内存泄漏,我们在上一章中已经介绍,内存泄漏是指不再使用的内存,没有被及时释放,导致内存被占用而可能导致程序崩溃的情况。那内存泄漏解决的方法就应该是怎么去及时的释放掉那些不再使用的内存(变量和对象)。在JS中,存在自动垃圾回收的机制可以实现对某些内存空间的自动释放,但是由于程序本身的逻辑问题,外部的因素,或者在处理大型数据集和复杂的应用程序时,内存泄漏是难以避免的。所以,JS还可以通过手动清除来实现内存的及时释放。因此,总的来说,在JS中,内存泄漏的解决方法可以分为自动和手动两种。

6.1 解决方法概述

  1. 自动——垃圾回收机制
    JS引擎中提供有自动的垃圾回收机制,无需像C++那样的底层语言对某些数据进行自动的释放。
  2. 手动
    当然面对一些难以避免的情况,我们可以通过下面的方法来进行手动的释放:
    • 置空来解除引用(最简单的方法)
      在JS中,只要一个变量不再被引用,它所占用的内存就会被自动回收。所以,如果需要手动释放内存,可以将不再需要的变量置空,也就是设置为 null,比如:

      let object = { name: "xiaobai_Ry" };
      object = null; // 置空,释放内存
      
    • 手动删除DOM节点
      通过JS动态生成的 DOM 节点,如果不在使用,也需要手动删除。可以通过 removeChild 方法【eg. element.parentNode.removeChild(element)】或者 innerHTML 属性【eg. element.innerHTML = ""】来删除 DOM 节点。此外,根据笔者我前面的笔记,我们也可以知道DOM节点再删除的时候,还需要注意游离DOM引用的问题。也就是我们在移除DOM节点的同时需要同步释放它对应的缓存引用,也就是说将相应的元素全部删除或者置空。不知道是否记得我们前面提到的例子,这里笔者也重新放到这里,加深一下印象呗~

      <div id="root">
        <ul id="ul">
          <li></li>
          <li id="liry"></li>
          <li ></li>
        </ul>
      </div>
      <script>
        let root = document.querySelector('#root')
        let ul = document.querySelector('#ul')
        let liry = document.querySelector('#liry')
        // 这里虽然在DOM树上使用 removechild 方法从根元素中删除了 ul 元素
        // 但是由于ul变量存在,所以整个ul及其子元素都不能垃圾回收
        root.removeChild(ul)
        // 虽然这里我们进一步置空了ul变量,但是因为liry变量仍然引用了ul的子节点
        // 说明对应元素还没有完全置空,所以ul元素依旧不能被垃圾回收
        ul = null
        // 到这里,所有对应的变量都置空,无引用
        // 此时可以立即垃圾回收掉整个 ul 元素及其子元素。
        liry = null
      </script>
      
    • 取消事件监听器
      当一个元素绑定了事件监听器后,需要手动取消绑定,以避免事件监听器对内存的持续占用。可以通过 removeEventListener 方法来取消事件监听器。这个我们在上章节的笔记中也有介绍,这里就不再赘述了。

    • 其他
      其实上面基本上的手动方法包括其他的一些手动清除方法,我们其实已经在上篇中的3.3内存泄漏可能的原因中有提及到,这里只是再做一个总结,其他方法就不在赘述了。

6.2 什么是垃圾

  • 对象不再被引用
  • 对象不能从根上访问到,也就是不可达

6.3 垃圾回收机制的定义及规则

JavaScript会在创建变量(对象、字符串)时自动分配内存,并在这些变量不被使用时自动释放内存,这个过程被称为垃圾回收(Garbage Collection),简称GC,也有的书叫做内存回收,因为回收的是不在使用的内存。

垃圾回收的规则,其实在上一篇章中,我们也算有讲过(就是在内存分析那一块),这里我直接放思维导图吧:
在这里插入图片描述

6.4 垃圾回收算法的基本流程

垃圾回收机制的通用流程:标记——》释放——》整理
(1)标记内存空间中的活动对象(正在使用的对象)和非活动对象(可以回收对象)
(2)删除非活动对象,释放内存空间
(3)整理内存空间,避免频繁回收后产生的大量内存碎片(不连续内存空间)

7. 垃圾回收的常见算法

对垃圾回收算法来说,核心思想就是如何判断内存已经不再使用,常用的基本垃圾回收算法有四种:引用计数(现代浏览器不再使用),标记清除(常用,V8老生代中用到),复制算法(V8新生代里用到)、标记整理(也就做标记压缩,V8老生代中用到)等

7.1 引用计数

在这里插入图片描述

  • 策略:跟踪每个变量被使用的次数
  • 描述:语言引擎有一张"引用表",保存了内存里面所有的资源(通常是各种值)的引用次数。每个对象都有一个引用计数器,当有对象引用它时,计数器+1;当引用失效时,计数器-1;任何时刻计数器为0时就是不可能再被使用的。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。
  • 优点: 引用计数为零时,发现垃圾立即回收;可以最大限度减少程序暂停
  • 缺点:需要计数器(引用表),空间开销较大,无法知道引用数量的上限;解决不了循环引用导致的无法回收问题

7.2 标记清除

在这里插入图片描述

  • 策略:分成标记和清除两个阶段,将不再使用对象定义为无法到达的对象,在标记完成后统一回收所有未被标记的对象。

  • 描述

    1. 遍历所有对象,找标记活动对象;
    2. 遍历所有对象,清除没有标记对象;
    3. 回收相应的空间。
  • 优点: 解决引用计数无法回收循环引用对象的问题,实现简单,一个二进制位数就可以为其标记,不需要额外的空间。

  • 缺点:垃圾回收之后出现内存碎片化,空闲内存块不连续,两次扫描,耗时严重,分配速度慢。

7.3 复制算法

在这里插入图片描述

  • 描述
    1. 将整个空间平均分成 from 和 to 两部分。
    2. 先在 from 空间进行内存分配,当空间被占满时,标记活动对象,并将其复制到 to空间。
    3. 复制完成后,将 from 和 to 空间互换。
  • 优点: 避免了内存碎片化的问题,吞吐量大,可以实现高速的分配
  • 缺点:典型的空间换取时间的算法,内存利用率低,浪费空间,不适合处理大型对象

7.4 标记整理(标记压缩)

在这里插入图片描述

  • 策略:分成标记,回收和整理三个阶段,其标记过程与标记清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
  • 描述
    1. 标记阶段与标记清除算法一样
    2. 清除阶段则会先执行整理,移动对象位置,将存活的对象移动到一边,然后再清理端边界外的内存。
  • 流程
    1. 从⼀个 GC root 集合出发,标记所有活动对象。
    2. 将所有活动对象移到内存的⼀端,集中到⼀起。
    3. 直接清理掉边界以外的内存,释放连续空间。
  • 优点: 解决标记清除中内存碎片化的问题,提高了内存的利用效率(解决复制算法的问题),适用于数据平滑、存活对象多的情况。
  • 缺点:移动对象位置,不会立即回收对象,需要对整个堆做多次搜索,回收的效率比较慢。

7.5 四种基本GC算法的总结

在这里插入图片描述

  • 内存效率:复制算法>标记清除算法>标记整理算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)。
  • 内存整齐度:复制算法=标记整理算法>标记清除算法。
  • 内存利用率:标记整理算法=标记清除算法>复制算法。

可以看出,效率上来说,复制算法是当之无愧的老大,但是却浪费了太多内存,而为了尽量兼顾上面所提到的三个指标,标记/整理算法相对来说更平滑一些,但效率上依然不尽如人意,它比复制算法多了一个标记的阶段,又比标记/消除多了一个整理内存的过程。

8. 垃圾回收机制的优化

上面我们讲了垃圾回收机制的四种基本算法,在后面其实针对上面的基本算法也有很多研究者提出来优化的策略,这里先简单的概述一下,主要包括:分代回收(这也是V8的垃圾回收策略),增量标记,惰性清理(空闲时间收集),并行回收和并发回收等

8.1 分代回收

注:这里后面V8详细介绍
将对象分为新生代对象和老生代对象两种,针对两种对象生命周期的特点采用不同的垃圾回收算法,以此有针对性地提高GC的回收效率。

8.2 增量回收

如果有很多对象,并且我们试图一次遍历并标记整个对象集,那么可能会花费一些时间,并在执行中会有一定的延迟。因此,引擎试图将垃圾回收分解为多个部分。然后,各个部分分别执行。这需要额外的标记来跟踪变化,这样有很多微小的延迟,而不是很大的延迟。
具体地,增量就是指将一次GC标记的过程分成很多小步,每执行完一小步就让应用逻辑执行一会儿,这样交替多次后完成一轮 GC 标记。
在这里插入图片描述
在V8中,增量标记具体采用是三色标记算法(暂停和恢复)和写屏障(增量中修改引用)来实现,也就是在进行垃圾回收的时候,并不会一次性把所有可达对象都标记出来,而是使用一个标记阶段(marking phase)和一个更新阶段(sweeping phase),将它们交替执行,逐步地完成标记工作。其中,三色标记法即使用每个对象的两个标记位和一个标记工作表来实现标记,两个标记位编码三种颜色:白【未被标记的对象】、灰【自身被标记,成员变量(该对象的引用对象)未被标记】、黑【自身和成员变量皆被标记】。三色标记法的 mark 操作可以渐进执行的而不需每次都扫描整个内存空间,可以很好的配合增量回收进行暂停恢复的一些操作,从而减少全停顿的时间。写屏障 (Write-barrier) 机制,即一旦有黑色对象引用白色对象,该机制会强制将引用的白色对象改为灰色,从而保证下一次增量 GC 标记阶段可以正确标记,这个机制也被称作 强三色不变性

8.3 惰性清理(空闲时间收集)

增量标记其实只是对活动对象和非活动对象进行标记,对于真正的清理释放内存 V8 采用的是惰性清理(Lazy Sweeping)。那惰性清理是什么?惰性清理是指系统在进行垃圾回收时,先将需要回收的对象标记出来,并不会立即进行回收,而是将其加入待清理队列中,等到系统空闲或者分配内存失败时才开始对待清理队列中的对象执行回收操作。这种方式可以减少GC操作对程序的影响,降低系统开销,提高程序的运行效率。因此,惰性清理算法可以通过监听空闲事件,利用浏览器空闲时间,在此期间进行回收处理。
相较于及时回收,惰性清理的优点是可以将回收操作分散到多个时间点上执行,避免了因为回收集中发生导致停顿的可能性;同时也改善了回收带来的性能瓶颈问题,使得程序在执行中更加平滑。而缺点是需要占用一定的内存空间,等待足够数量的对象达到一定阈值方可进行回收。

8.4 并行回收

并行回收指的是垃圾回收器在主线程上执行的过程中,开启多个辅助线程,同时执行同样的回收工作,以此i加快回收速度。
在这里插入图片描述

  • 优点:
    • 高吞吐量:由于使用了多个cpu同时进行回收操作,所以并行回收可以提供更高的吞吐量。
    • 低延迟:并行回收通常会与应用程序并发运行,因此对延迟的影响相对较小。
    • 硬件利用率高:并行回收可以最大限度地利用多核处理器的性能。
  • 缺点:
    • 需要更多的内存资源:由于垃圾回收器需要同时处理许多对象,因此并行回收需要更多的内存资源。
    • 停顿时间较长:虽然并行回收不会完全停止应用程序,但在回收期间仍然需要暂停一段时间,因此会对应用程序的延迟产生影响。

8.5 并发回收

并发回收指的是主线程在执行 JavaScript 的过程中,辅助线程能够在后台完成执行垃圾回收的操作,辅助线程在执行垃圾回收的时候,主线程也可以自由执行而不会被挂起。
在这里插入图片描述

  • 优点:
    • 低延迟:与并行回收类似,并发回收也可以最小化应用程序运行期间的停顿时间。
    • 更少的内存资源:由于垃圾回收器只需处理较少数量的对象,因此需要的内存资源也较少。
  • 缺点:
    • 低吞吐量
    • 线程切换繁琐:并发回收需要协调线程之间的信息交换,所以实现较为复杂,而且容易出现并发问题。

9. V8的垃圾回收策略

在这里插入图片描述

v8引擎垃圾回收策略采用分代回收的思想,也就是将内存(堆)分为新生代和老生代两个区域,分别使用副、主垃圾回收器。其中,新生代中的对象为存活时间较短的对象,采用复制算法(Scavenge 算法)加标记整理算法,而Scavenge 算法的具体实现,主要采用了Cheney算法。老生代中的对象为存活时间较长或常驻内存的对象,采用标记清除和标记压缩的算法结合。对象最开始都会先被分配到新生代(如果新生代内存空间不够,直接分配到老生代),新生代中的对象会在满足某些条件后,被移动到老生代,这个过程也叫晋升。通过主垃圾回收其来实现老生代的垃圾回收,同时为了不造成卡顿,标记的过程被切分为一个个子标记,交替进行(也就是增量标记)。
在这里插入图片描述

9.1 V8新生代的垃圾回收

新生代存的对象都是生存周期短的对象,回收新生代对象主要采用的是复制算法(Scavenge 算法)加标记整理算法。而Scavenge 算法的具体实现,主要采用了Cheney算法。Cheney算法将新生代内存空间分成From和To两个等大的空间,其中,To空间(空闲区)处于闲置状态而From空间(使用区)处于使用状态。新创建的对象会被存到From空间,当From空间满的时候就执行复制算法(Scavenge 算法)进行垃圾回收。其中垃圾回收算法具体流程为:先对From空间中的对象进行标记,完成后将标记对象复制到To空间的⼀端,然后将两个区域角色反转,就完成了回收操作。
在这里插入图片描述
这个标记主要是指:检查From空间中的存活对象,如果对象存活则判断对象是否满足晋升到老生代的条件,如果满足条件则晋升到老生代。如果不满足条件则移动到 To空间。

在新生代GC中,每次执行清理都需要复制对象,所以需要时间成本所以新生代空间一般会设置得比较小(1~8M)。

新生代对象晋升到老生代有两个条件:

  1. 第一个是判断对象是否已经经过一次 Scavenge 回收。若经历过,则将对象从 From 空间复制到老生代中;若没有经历,则复制到 To 空间。当⼀个对象经过多次复制后依然存活,它将会被认为是生命周期较长的对象,随后会被移动到老生代中,采用老生代的垃圾回收策略进行管理
    在这里插入图片描述

  2. 第二个是 To 空间的内存使用占比是否超过限制。当对象从 From 空间复制到 To 空间时,若 To 空间使用超过 25% ,则对象直接晋升到老生代中。设置 25% 的原因主要是因为算法结束后,两个空间结束后会交换位置,如果 To 空间的内存太小,会影响后续的内存分配。
    在这里插入图片描述

9.2 V8老生代的垃圾回收

老生中的对象一般存活时间较长且数量也多,使用了两个算法,分别是标记清除算法和标记压缩算法。由于Mark-Conpact需要移动对象,所以它的执行速度不可能很快,在取舍上,V8主要使用标记清除,在空间不足以对从新生代中晋升过来的对象进行分配时,才使用标记整理。

老生代总结: 回收老生代对象主要采用标记清除标记整理增量标记算法,主要使用标记清除算法,只有在内存分配不足时,采用标记整理算法。

  1. 首先使用标记清除完成垃圾空间的回收
  2. 采用标记整理进行空间优化
  3. 采用增量标记进行效率优化
    在这里插入图片描述

背景

在老生代中,存活对象占较大比重,如果继续采用Scavenge算法进行管理,就会存在两个问题:

  1. 由于存活对象较多,复制存活对象的效率会很低。
  2. 采用Scavenge算法会浪费一半内存,由于老生代所占堆内存远大于新生代,所以浪费会很严重。

💡 Scavenge只复制活着的对象,而标记清除(Mark-Sweep)只清除死了的对象。活对象在新生代中只占较少部分,死对象在老生代中只占较少部分,这就是两种回收方式都能高效处理的原因。

描述: 标记清除法首先会对内存中存活的对象进行标记,标记结束后清除掉那些没有标记的对象。由于标记清除后会造成很多的内存碎片,不便于后面的内存分配。所以为了解决内存碎片的问题引入了标记压缩法。

优化背景: 由于JS是单线程运行的,意味着垃圾回收算法和脚本任务在同⼀线程内运行,在执行垃圾回收时,后续脚本任务需要等垃圾回收完成后才能继续执行。若堆中的数据量非常大,⼀次完整垃圾回收的时间会非常长,导致应⽤的性能和响应能力直线下降。为了避免垃圾回收影响应用的性能,V8 将标记的过程拆分成多个⼦标记,让垃圾回收标记和应用逻辑交替执行,避免脚本任务等待较长时间。

优化: 由于在进行垃圾回收的时候会暂停应用的逻辑,对于新生代方法由于内存小,每次停顿的时间不会太长,但对于老生代来说每次垃圾回收的时间长,停顿会造成很大的影响。为了解决这个问题 V8 引入了增量标记的方法,将一次停顿进行的过程分为了多步,每次执行完一小步就让运行逻辑执行一会,就这样交替运行。

在老生代中,以下情况会先启动标记清除算法:

  • 某一个空间没有分块的时候
  • 空间中被对象超过一定限制
  • 空间不能保证新生代中的对象移动到老生代中

9.3 总结

新生代由于占用空间比较少,采用空间换时间机制。而老生代区域空间比较大,不太适合大量的复制算法和标记整理,所以最常用的是标记清除算法,为了就是让全停顿的时间尽量减少。

10. 内存管理的性能优化

这里其实也可以参考上篇章中可能原因里面的解决方案。

  1. 避免使用全局变量和函数
    全局变量和函数在使用前不需要声明,但是会影响代码的可读性和维护性,同时也容易被误用和滥用。为了减少内存开销和保证代码的可靠性,尽可能避免使用全局变量和函数,并采用模块化编程方式。
    • 全局变量会挂载在window下
    • 全局变量至少有一个引用计数
    • 全局变量存活更久,持续占用内存
    • 在明确数据作用域的情况下,尽量使用局部变量
  2. 减少内存泄漏
    javascript 的回收机制会自动回收无法被引用的变量和对象。但是如果有一部分变量或对象被持续引用却不再使用了(例如全局变量等),就会造成内存泄漏的问题。因此在编写代码时,需要注意及时清理不再需要的变量和对象,并避免产生闭包、循环引用等问题。
    • 事件绑定优化
    • 避开闭包陷阱
    • 减少循环体中的活动
    • …(见上章中的3.3)
  3. 减少对象创建和销毁
    javascript 中创建对象相比其他语言比较容易,但是同时也很容易产生大量的**对象。为了减少对象的创建和销毁,可以使用对象池技术,预先创建多个对象并保存在池中,需要使用时从池中取出,并在使用完成后重新放回池中。
  4. 使用静态类型
    javascript 是一种弱类型语言,由于类型转换的不确定性会造成较大的内存开销。从 ecmascript 6 开始,引入了静态类型系统,可以通过声明静态类型来减少类型转换和检测的开销,从而提高代码运行效率。
  5. 减少判断层级
  6. 减少数据读取次数
    对于频繁使用的数据,我们要对数据进行缓存。
  7. 使用内存快照工具进行调试
    在开发过程中,可以使用内存快照工具对代码进行内存占用

相关博客推荐

华为云——Vue进阶(幺陆玖):JS垃圾回收机制
知乎——V8 垃圾回收原来这么简单?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/476871.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

十大经典排序算法总结(C语言版本)

前言&#xff1a;排序算法是最经典的算法知识&#xff0c;也是每个合格程序员应该需要掌握的知识点。考虑到排序算法通常代码简短&#xff0c;逻辑思维强和应用范围广等特性&#xff0c;排序算法题目便成为了面试中的常客。在面试中最常考的是快速排序和归并排序等基本的排序算…

SQL(基础)

DDL: 数据定义语言 Definition&#xff0c;用来定义数据库对象&#xff08;数据库、表、字段&#xff09;CREATE、DROP、ALTER DML: 数据操作语言 Manipulation&#xff0c;用来对数据库表中的数据进行增删改 INSERT、UPDATE、DELETE 注意&#xff1a; DDL是改变表的结构 DML…

一以贯之:从城市网络到“城市一张网”

《论语里仁》中子曰&#xff1a;“参乎&#xff0c;吾道一以贯之”。 孔子所说的“一以贯之”&#xff0c;逐渐成为了中国文化与哲学的重要组成部分&#xff0c;指明事物发展往往需要以标准化、集约化、融合化作为目标。这种智慧在数字化发展中格外重要。从云计算、大数据技术模…

一个快速去除黑背景和其他颜色背景,生成透明PNG图的小工具

做粒子效果或者其他一些图案的时候&#xff0c;时常能找到不少原图&#xff0c;但是却有黑色的背景或者其他背景色&#xff0c;导致用起来比较麻烦。这个小工具就可以方便的去除黑背景&#xff0c;生成透明PNG图&#xff0c;可以把想要的图案方便的取出来。 链接请见&#xff…

【Arduino 和 DS3231 实时时钟教程】

【Arduino 和 DS3231 实时时钟教程】 1. 概述2. 原理分析3. DS3231 实时时钟4. 编程1. 概述 在本Arduino教程中,我们将学习如何使用DS3231实时时钟模块。您可以观看以下视频或阅读下面的书面教程。 2. 原理分析 这里出现的第一个问题是,当Arduino本身具有内置计时器时,为什…

利用倾斜摄影超大场景的三维模型轻量化技术如何提高网络传输的效率?

利用倾斜摄影超大场景的三维模型轻量化技术如何提高网络传输的效率&#xff1f; 倾斜摄影超大场景的三维模型轻量化在网络传输中的效率可以通过以下几个方面进行提高&#xff1a; 一、数据压缩 对于倾斜摄影超大场景的三维模型数据&#xff0c;可以采用数据轻量化压缩技术进…

如何利用ChatPDF快速阅读英文论文,帮你写文章

如何利用ChatPDF快速阅读英文论文&#xff0c;帮你写文章 英语渣狂喜&#xff5e;确实惊艳到我了&#xff01; 使用平台&#xff1a;https://www.chatpdf.com/ 1、上传PDF 访问官网&#xff1a;https://www.chatpdf.com/&#xff0c;界面很美&#xff0c;点击直接上传 PDF&…

Python小姿势 - Python学习笔记——类与对象

Python学习笔记——类与对象 类与对象是面向对象编程的两个基本概念。类是对象的抽象概念&#xff0c;对象是类的具体表现。 类是对一类事物的抽象&#xff0c;它是描述一类事物的模板&#xff0c;而对象是类的具体表现。对象是类的实例&#xff0c;类是对象的模板。 举个例子&…

【Midjourney】Midjourney 连续性人物创作 ④ ( 使用 URL + Seed 随机种子生成连续性的人物 )

文章目录 一、生成图片并获取 Seed二、使用 URL Seed 随机种子生成连续性的人物 使用 URL 链接 和 Seed 随机种子 生成连续性人物 , 必须先生成一组图片 , 然后按 U 按钮 , 选择一张大图 , 之后所有的连续性人物图片都基于该图片进行生成 ; 使用 URL Seed 随机种子生成连续性…

吴恩达与 OpenAI 联手打造《ChatGPT 提示工程》中文版教程,开启智能对话新篇章!

ChatGPT 的使用技巧 ChatGPT 上线至今&#xff0c;已经快5个月了&#xff0c;但是不少人还没真正掌握它的使用技巧。 其实&#xff0c;ChatGPT 的难点&#xff0c;在于 Prompt&#xff08;提示词&#xff09;的编写&#xff0c;OpenAI 创始人在今年2月时&#xff0c;在Twitter…

数据结构学习分享之顺序表详解

数据结构第二课 1. 前言2. 线性表3. 顺序表3.1 概念以及结构3.11 静态顺序表3.12 动态顺序表 4. 顺序表的实现4.1 顺序表内容的命名4.2 初始化结构4.3 初始化函数4.4 扩容函数4.5 尾插函数4.6 打印函数4.7 尾删函数4.8 头插函数4.9 头删函数4.10 销毁顺序表 5. 写代码时应该注意…

安装python以及编辑器pycharm

文章目录 前言一、安装python1.安装python2.测试python3.查看环境变量 二、安装Pycharm1.下载Pycharm2.查看环境变量3.测试 总结 前言 python是重要的程序语言之一 本文介绍如何安装python&#xff0c;以及如何安装python编辑器----Pycharm 一、安装python 1.安装python 首先…

SpringCloud_OpenFeign服务调用和Resilience4J断路器

文章目录 一、负载均衡概论1、服务器负载均衡2、客户端负载均衡3、客户端负载均衡策略(SpringCloudRibbon)4、客户端负载均衡策略(SpringCloudLoadBalancer) 二、SpringCloudOpenFeign服务调用1、OpenFeign服务调用的使用2、OpenFeign服务调用的日志增强3、OpenFeign服务调用超…

下载——安装——使用FinalShell

下载——安装——使用FinalShell FinalShell简介&#xff1a;下载&#xff1a;使用&#xff1a; FinalShell简介&#xff1a; FinalShell是一款免费的国产的集SSH工具、服务器管理、远程桌面加速的软件&#xff0c;同时支持Windows&#xff0c;macOS&#xff0c;Linux&#xf…

总结吴恩达 ChatGPT Prompt 免费课程

吴恩达联合 OpenAI 官方&#xff0c;发布了免费的 ChatGPT Prompt 视频教程。 链接&#xff1a;https://learn.deeplearning.ai/chatgpt-prompt-eng/lesson/2/guidelines 视频纯英文&#xff0c;小姐姐的英伦腔&#xff0c;听得很舒服。 我看了第一集&#xff0c;讲了四个技巧&…

阿里云国际版ACE与国内版ACE区别

1.国际版ACE与国内版ACE有哪些不同 2.国际版ACP/ACE约考流程 2.1 登录VUE官方网站约考 https://www.pearsonvue.com.cn/Clients/Alibaba-Cloud-Certification.aspx ​ 2.2 如果之前有注册过账户&#xff0c;那就直接登录&#xff0c;如果还没有账户&#xff0c;那就创建账户 2.…

React之动态渲染菜单(无状态机)

首先如果需要动态渲染菜单&#xff0c;并且根据不同用户获取不同的菜单&#xff0c;需要下面步骤&#xff1a; 我们动态渲染菜单需要使用递归的方式 1️⃣&#xff1a;首先用户的菜单都是从后端获取的&#xff0c;所以需要先发送请求&#xff0c;拿到菜单&#xff0c;然后将数…

AI 命名之羊驼

转眼进入 AI 时代&#xff0c;ChatGPT 吹起了一股大语言模型之风&#xff0c;恐怕羊驼们绝不曾想到&#xff0c;自己的种族竟也被卷入其中。 AI 产品的命名一向偏好晦涩的缩写。GPT&#xff08;Generative Pre-trained Transformers&#xff09;已经是最简明直白的一类。相比之…

gRPC入门教程

1.简介 gRPC是Google开发的一个跨平台、开源的远程过程调用(RPC)框架&#xff0c;可以使用Protocol Buffers作为接口定义语言(IDL)和底层消息交换格式。 在gRPC中&#xff0c;客户端应用程序可以直接调用位于不同机器上的服务器应用程序的方法&#xff0c;就像本地对象一样&a…

【Java笔试强训 2】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;排序子…