细数6种垃圾回收器的区别, 快进来看看有没有你要用的

2023-10-27


前言

大家好啊, 这里是Yve菌, 今天给大家详细介绍一下我们在进行GC垃圾回收时的主力军-垃圾回收器. 由于我们使用的jdk版本有不同, 使用的堆内存大小也不同, 这个时候我们可能就会用到不同的垃圾回收器. 垃圾回收器目前来说在市面上会有很多, 今天我们就来详细讲讲以下垃圾收集器: Serial, Parallel Scavenge, Parnew, CMS, G1, ZGC.
常用垃圾回收器


一、垃圾回收算法

在介绍垃圾回收器之前先普及一下垃圾回收算法, 每个垃圾回收器都在进行垃圾回收时都会用到一定的垃圾回收算法, 垃圾回收算法有四种

1.复制算法

在复制算法中, 堆内存空间会被分成两块, 一块用来存储对象, 另一块不使用. 当需要进行垃圾回收时, 垃圾回收器会将存活的对象通过复制的方法到另一块没有使用的区域中, 之后直接将之前使用的区域进行整体清除.
复制算法

2.标记-清除算法

标记清除算法会将存活的对象进行标记, 在进行gc时直接清除掉标记的对象.

这是最基础的收集算法, 但是很显然也带来了一个问题: 内存碎片.
标记清除算法

3. 标记-整理算法

在标记整理算法中也同样会对存活的对象进行标记, 而与标记-清除不同的是, 标记整理算法会将所有存活对象移动到一端, 然后直接清理掉端边界以外的区域.
标记整理算法

4.分代算法

分代算法是一个概念, 即以分代为界限, 根据不同的分代情况使用不同的分代算法. 通常来说年轻代使用复制算法, 老年代使用标记清除或者标记整理算法.

二、Serial收集器

Serial收集器是最基本、历史最悠久的垃圾收集器了。他只采用了一个线程来进行gc并且在gc时其他所有的用户线程都会停止(STW), 效率相对来说非常低.

Serial分为年轻代版本(Serial)和老年代版本(Serial Old), 年轻代使用参数:-XX:+UseSerialGC 老年代则使用参数: -XX:+UseSerialOldGC. Serial老年代版本也是CMS和G1的后备方案.

Serial的年轻代采用复制算法, 老年代采用标记整理算法.
Serial回收流程

三、Parallel Scavenge收集器

Parallel Scavenge可以理解为Serial收集器的多线程版本, 他在进行垃圾回收时会使用多个线程, 效率相比Serial会高很多, 默认的收集线程数跟cpu核数相同,当然也可以用参数(-XX:ParallelGCThreads)指定收集线程数,但是一般不推荐修改。

Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值。

Parallel同样也分为年轻代版本(Parallel Scavenge)和老年代版本(Parallel Old), 使用参数 -XX:+UseParallelGC-XX:+UseParallelOldGC激活相应版本.

Parallel年轻代使用复制算法, 老年代使用标记整理算法.
Parallel回收流程

四、ParNew收集器

ParNew和Parallel收集器基本相同, 主要是可以和CMS配合使用. 它是许多运行在Server模式下的虚拟机的首要选择,除了Serial收集器外,只有它能与CMS收集器(真正意义上的并发收集器,后面会介绍到)配合工作。

ParNew使用参数: -XX:+UseParNewGC, 年轻代使用复制算法, 老年代使用标记整理算法.
Parnew回收流程

五、CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用,它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

1. GC流程

我们从名字中的Mark Sweep可以得知, CMS使用的是标记-清除算法. 它的垃圾回收流程比之前的垃圾收集器更复杂, 分为五个步骤.

  1. 初始标记(STW)
    在这个阶段中, 暂停所有其他的线程(STW), 垃圾回收器会先扫描GCRoots的直接引用, 速度很快
  2. 并发标记
    在这个阶段, 其他线程恢复执行, 同时垃圾回收器会继续遍历扫描已经被标记的GCRoots的子引用们, 期间用户线程可能会产生新的垃圾
    这是GC流程中最长的一段时间, 但是由于用户线程和GC线程同时运行, 期间不会发生STW.
  3. 重新标记(STW)
    这个阶段主要是为了解决在并发标记中产生的新的变动以及漏标等问题. 这个阶段也会STW, 时间可能会比初始标记稍长, 但是远小于并发标记. 主要用到了三色标记中的增量更新算法
  4. 并发清理
    其他线程继续运行, gc线程同步对未标记的区域进行垃圾回收.
  5. 并发重置
    将之前进行的标记符号进行重置

CMS回收流程

2. CMS的优缺点

优点:

  • 相对于Parallel Scavenge收集器来说, CMS更注重于用户的体验, 他将STW的时间尽量的缩到最小, 虽然整个垃圾处理流程加起来的总时间可能会较长, 但是用户的体验比较好.

缺点:

  • 对CPU资源敏感(可能会和其他线程抢占资源)
  • 无法处理浮动垃圾(在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了)
  • CMS采用标记-清除算法, 这样会造成内存碎片, 我们可以通过参数-XX:+UseCMSCompactAtFullCollection让jvm在执行完清除之后自己整理
  • 由于用户线程和gc线程并发, 可能会导致垃圾还没清理完, 然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发full gc,也就是"concurrent mode failure",此时会进入STW,用serial old垃圾收集器来回收

3. CMS参数

  • -XX:+UseConcMarkSweepGC:启用cms
  • -XX:ConcGCThreads:并发的GC线程数
  • -XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(减少碎片)
  • -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次
  • -XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
  • -XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
  • -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,降低CMS GC标记阶段(也会对年轻代一起做标记,如果在minor gc就干掉了很多对垃圾对象,标记阶段就会减少一些标记时间)时的开销,一般CMS的GC耗时 80%都在标记阶段
  • -XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW
  • -XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;

六、G1收集器

G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征.

1. 内存布局

在G1中内存布局发生了一些变化, 他并未使用之前的明确分代概念, 而改为了将Java堆划分为多个大小相等的独立区(Region). JVM目标是不超过2048个Region(JVM源码里TARGET_REGION_NUMBER 定义),实际可以超过该值,但是不推荐。

一般Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以用参数-XX:G1HeapRegionSize手动指定Region大小,但是推荐默认的计算方式。

默认年轻代对堆内存的占比是5%,如果堆大小为4096M,那么年轻代占据200MB左右的内存,对应大概是100个Region,可以通过-XX:G1NewSizePercent设置新生代初始占比,在系统运行中,JVM会不停的给年轻代增加更多的Region,但是最多新生代的占比不会超过60%,可以通过-XX:G1MaxNewSizePercent调整。年轻代中的Eden和Survivor对应的region也跟之前一样,默认8:1:1,假设年轻代现在有1000个region,eden区对应800个,s0对应100个,s1对应100个。

Region是可以动态变化的, 当一个Region没被占用时, 他既可以存放老年代对象, 也可以存放年轻代对象.

G1垃圾收集器对于对象什么时候会转移到老年代跟之前讲过的原则一样,唯一不同的是对大对象的处理,G1有专门分配大对象的Region叫Humongous区,而不是让大对象直接进入老年代的Region中。在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%,比如按照上面算的,每个Region是2M,只要一个大对象超过了1M,就会被放入Humongous中,而且一个大对象如果太大,可能会横跨多个Region来存放。

Humongous区专门存放短期巨型对象,不用直接进老年代,可以节约老年代的空间,避免因为老年代空间不够的GC开销。
Full GC的时候除了收集年轻代和老年代之外,也会将Humongous区一并回收。

G1内存布局

2. GC流程

  1. 初始标记(initial mark,STW)
    与CMS基本相同, 暂停所有其他的线程(STW), 将GCRoots的直接引用进行标记, 速度很快
  2. 并发标记(Concurrent Marking)
    与CMS基本相同, 其他线程恢复执行, 同时垃圾回收器会继续遍历扫描已经被标记的GCRoots的子引用们, 期间用户线程可能会产生新的垃圾
  3. 最终标记(Remark,STW)
    与CMS的重新标记相同, 主要是为了解决在并发标记中产生的新的变动以及漏标等问题.这个阶段也会STW, 时间可能会比初始标记稍长, 但是远小于并发标记.
  4. 筛选回收(Cleanup,STW)
    这是G1特有的阶段, 筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿STW时间(可以用JVM参数 -XX:MaxGCPauseMillis指定)来制定回收计划,比如说老年代此时有1000个Region都满了,但是因为根据预期停顿时间,本次垃圾回收可能只能停顿200毫秒,那么通过之前回收成本计算得知,可能回收其中800个Region刚好需要200ms,那么就只会回收800个Region(Collection Set,要回收的集合),尽量把GC导致的停顿时间控制在我们指定的范围内。这个阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。G1的回收算法是复制算法,将一个region中的存活对象复制到另一个region中,因此回收后几乎不会有太多内存碎片

G1回收流程

G1收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region(这也就是它的名字Garbage-First的由来),比如一个Region花200ms能回收10M垃圾,另外一个Region花50ms能回收20M垃圾,在回收时间有限情况下,G1当然会优先选择后面这个Region回收。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限时间内可以尽可能高的收集效率。

3. 特点

  • 并行与并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
  • 分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。
  • 空间整合:与CMS的“标记–清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。
  • 可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1 和 CMS 共同的关注点,但G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段(通过参数-XX:MaxGCPauseMillis指定)内完成垃圾收集。

4. 垃圾收集分类

  • YoungGC
    与其他垃圾收集器的YoungGC不同的是, 当G1的Region中的年轻代对象满了之后, 他会先进行一次检测, 如果回收Eden区的对象预计时间远远小于设定参数 -XX:MaxGCPauseMills时, 不会立刻进行回收, 而是会使用新的Region来继续存放年轻代对象
    , 直到回收年轻代对象的预计时间接近设定值才会触发YoungGC.

  • Mixed GC
    老年代的堆占有率达到参数-XX:InitiatingHeapOccupancyPercent时触发, 回收所有年轻代的对象, 以及部分老年代的对象(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及大对象区. 过程采用复制算法, 当Region里对象复制时没有足够的Region存放会触发FullGC

  • FullGC
    停止系统程序,然后采用单线程(Serial)进行标记、清理和压缩整理,好空闲出来一批Region来供下一次MixedGC使用,这个过程是非常耗时的

5. 参数

  • -XX:+UseG1GC:使用G1收集器
  • -XX:ParallelGCThreads:指定GC工作的线程数量
  • -XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的N次幂),默认将整堆划分为2048个分区
  • -XX:MaxGCPauseMillis:目标暂停时间(默认200ms)
  • -XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%,值配置整数,默认就是百分比)
  • -XX:G1MaxNewSizePercent:新生代内存最大空间
  • -XX:TargetSurvivorRatio:Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代
  • -XX:MaxTenuringThreshold:最大年龄阈值(默认15)
  • -XX:InitiatingHeapOccupancyPercent:老年代占用空间达到整堆内存阈值(默认45%),则执行新生代和老年代的混合收集(MixedGC),比如我们之前说的堆默认有2048个region,如果有接近1000个region都是老年代的region,则可能就要触发MixedGC了
  • -XX:G1MixedGCLiveThresholdPercent(默认85%) region中的存活对象低于这个值时才会回收该region,如果超过这个值,存活对象过多,回收的的意义不大。
  • -XX:G1MixedGCCountTarget:在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一会,然后暂停回收,恢复系统运行,一会再开始回收,这样可以让系统不至于单次停顿时间过长。
  • -XX:G1HeapWastePercent(默认5%): gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立即停止混合回收,意味着本次混合回收就结束了。

6. 适用场景

  • 50%以上的堆被存活对象占用
  • 对象分配和晋升的速度变化非常大
  • 垃圾回收时间特别长,超过1秒
  • 8GB以上的堆内存(建议值)
  • 停顿时间是500ms以内

七、ZGC

ZGC(The Z Garbage Collector)是JDK 11中推出的一款追求极致低延迟的垃圾收集器,它曾经设计目标包括:

  • 停顿时间不超过10ms(JDK16已经达到不超过1ms);
  • 停顿时间不会随着堆的大小,或者活跃对象的大小而增加;
  • 支持8MB~4TB级别的堆,JDK15后已经可以支持16TB。

1. 内存布局

ZGC彻底抛弃了堆内存中的分代概念, 而是采用了基于Region的内存布局.
Region具有小, 中, 大三类容量:

  • 小型Region(Small Region): 容量固定大小为2M, 用于存放小于256KB的小对象
  • 中型Region(Medium Region): 容量固定为32M, 用于存放256KB-4M之间的对象
  • 大型Region(Large Region): 容量不固定, 但是必须为2M的倍数, 用来存放4M及4M以上的大对象, 并且一个大Region只能存一个大对象.
    ZGC内存布局

2. NUMA-aware

NUMA对应的有UMA,UMA即Uniform Memory Access Architecture,NUMA就是Non Uniform Memory Access Architecture。UMA表示内存只有一块,所有CPU都去访问这一块内存,那么就会存在竞争问题(争夺内存总线访问权),有竞争就会有锁,有锁效率就会受到影响,而且CPU核心数越多,竞争就越激烈。NUMA的话每个CPU对应有一块内存,且这块内存在主板上离这个CPU是最近的,每个CPU优先访问这块内存,那效率自然就提高了
NUMA

3. 回收流程

ZGC回收流程

ZGC的回收流程可以分为两个大阶段:

  1. 标记阶段
    总体来说标记阶段与之前的垃圾回收器相同, 但是区别是, 之前的垃圾回收器是会在对象的对象头中标记, 而ZGC则是会更新颜色指针中的Mark0Mark1
    1.1 初始标记(STW): 同CMS, G1. 暂停所有其他的线程(STW), 将GCRoots的直接引用进行标记, 速度很快
    1.2 并发标记: CMS, G1, 其他线程恢复执行, 同时垃圾回收器会继续遍历扫描已经被标记的GCRoots的子引用们, 期间用户线程可能会产生新的垃圾
    1.3 再标记(STW): 与CMS的重新标记相同, 主要是为了解决在并发标记中产生的新的变动以及漏标等问题.这个阶段也会STW, 时间可能会比初始标记稍长, 但是远小于并发标记.

  2. 转移阶段
    2.1 并发转移准备: 在这个阶段, ZGC会分析出哪些Region需要清理和转移
    2.2 初始转移(STW): 暂停其他线程, 转移之前分析出的初始对象, 并直接进行重映射
    2.3 并发转移: 恢复其他线程, 同时转移初始对象的子引用, 并维护一个转发表来记录这些对象转移之后的位置.

重映射
在转移阶段中我们通过维护一张转发表来记录对象转移之后的位置, 但是其他对象指向的地址未改变还是原引用, 对象在访问原引用之前会通过读屏障读取转发表中信息找到对象转移后的位置. 在进行下一次gc标记之前, 会将之前转发表中记录的对象进行重映射, 也就是把原对象的位置改为新的位置, 之后把转发表中数据清空.

4. 颜色指针

Colored Pointers,即颜色指针,如下图所示,ZGC的核心设计之一。以前的垃圾回收器的GC信息都保存在对象头中,而ZGC的GC信息保存在指针中。
颜色指针
每个对象有一个64位指针,这64位被分为:

  • 18位:预留给以后使用;
  • 1位:Finalizable标识,此位与并发引用处理有关,它表示这个对象只能通过finalizer才能访问;
  • 1位:Remapped标识,设置此位的值后,对象未指向relocation set中(relocation set表示需要GC的Region集合);
  • 1位:Marked1标识;
  • 1位:Marked0标识,和上面的Marked1都是标记对象用于辅助GC;
  • 42位:对象的地址(所以它可以支持2^42=4T内存)

5. ZGC存在的问题

ZGC最大的问题是浮动垃圾。ZGC的停顿时间是在10ms以下,但是ZGC的执行时间还是远远大于这个时间的。假如ZGC全过程需要执行10分钟,在这个期间由于对象分配速率很高,将创建大量的新对象,这些对象很难进入当次GC,所以只能在下次GC的时候进行回收,这些只能等到下次GC才能回收的对象就是浮动垃圾。

6. 参数

ZGC参数设置
启用ZGC比较简单,设置JVM参数即可:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC。调优也并不难,因为ZGC调优参数并不多,远不像CMS那么复杂。它和G1一样,可以调优的参数都比较少,大部分工作JVM能很好的自动完成。下图所示是ZGC可以调优的参数:
ZGC参数


总结

我们了解到了这么多垃圾收集器, 那么我们怎么去选择使用呢?

  • 优先调整堆的大小让服务器自己来选择
  • 如果内存小于100M,使用串行收集器
  • 如果是单核,并且没有停顿时间的要求,串行或JVM自己选择
  • 如果允许停顿时间超过1秒,选择并行或者JVM自己选
  • 如果响应时间最重要,并且不能超过1秒,使用并发收集器
  • 4G以下可以用parallel,4-8G可以用ParNew+CMS,8G以上可以用G1,几百G以上用ZGC

以上就是本期的内容啦, 如果能帮到你麻烦点个赞呗! 感谢大家!

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

细数6种垃圾回收器的区别, 快进来看看有没有你要用的 的相关文章

随机推荐

  • letcode算法题集锦

    day01 letcode9 买卖股票的最佳时机 给定一个数组 prices 它的第 i 个元素 prices i 表示一支给定股票第 i 天的价格 你只能选择 某一天 买入这只股票 并选择在 未来的某一个不同的日子 卖出该股票 设计一个算
  • ec服务器如何显示4d,ECToolbox软件四种公式在心肌灌注显像中计算左心室射血分数的应用...

    摘要 目的 比较ECToolbox 4种公式 R 0 R 1 R 2 R 3 计算心电图门控心肌灌注显像 ECG gated myocardial perfusion SPECT GMPS 左心室射血分数与平衡法心血池显像 equilibr
  • el-input (输入框)校验 只能输入数字和小数

    备注 我写的是判断输入的是否数字或小数 如果只需判断是否是数字可直接复制elementui官网上的案例即可 1 一定要写 prop aa 不然是没效果的 2 仿照elementui官网 绑定rules 3 写校验 tirgger chang
  • gcj Round 1- A -B题

    输入 E R N v1 v2 vn 有n个工作 没做一个消耗一定的能量 但可以得到R个能量 最多为E 多则去除 初始能量是E 每件工作的收益是 v i u i u i 做i使用的能量 u i gt 0 u i lt E 解 如果规定u i
  • VUE layui之table数据表格使用详细说明

    步骤 一 layui官网下载包 二 将解压的文件放入static文件夹 如果没有就在根目录下新建一个 三 public文件下index html中引入css及js文件 这里注意顺序和你下载的版本号 四 vue页面中使用
  • java3D 第三章 java3D基本图形类详解

    第三章 java 3D基本图形功能 java 3D基本图形功能 java 3D场景式管理 SimpleUniverse类及其方法 ViewingPlatform类及方法 包的关系 Shape3D类及方法 Appearance类及方法 Bra
  • 大数据技术——Scala语言基础

    Scala语言概述 计算机的缘起 数学家阿隆佐 邱奇 Alonzo Church 设计了 入演算 这是一套用于研究函数定义 函数应用和递归的形式系统 入演算被视为最小的通用程序设计语言 入演算的通用性就体现在 任何一个可计算函数都能用这种形
  • 对于stm32,初学者用库函数好还是直接对寄存器操作比较好

    在stm32教学光盘的A里 有两个开发指南 一个是库函数版本 一个是寄存器版本 那么问题来了 作为一个初学者 我应该用库函数好还是直接对寄存器操作比较好 为此我搜集了一些资料 找到了一些可以借鉴的文章 首先 两个都是C语言 从51过渡过来的
  • PBFT代码实现

    本篇文章主要是PBFT共识的简单实现 其中有许多地方都做了简化 PBFT的原理已在上篇文章中描述过 如果对PBFT的原理不太清晰的的可以进行查看 文章地址 共识算法学习总结 代码实现的主要功能有 通过客户端添加区块 使用libp2p的mdn
  • CVTE笔试面试经验分享(硬件)—2020秋招

    秋招流程 投简历 在线笔试 技术面试一 技术面试二 综合面试 投简历 简历是直接在CVTE的校招网上投递的 然后可以选择面试城市和笔试时间 在线笔试 简历筛选通过后就会通知进行线上的笔试 笔试结果各个岗位不同等待的也不同 硬件笔试都是基础
  • 【Linux】基本指令(一)

    目录 一 ls指令 1 不指定目录 ls 2 指定目录 ls huangchao 3 加选项 ls l 4 加选项 ls a 5 选项组合 ls l a 6 选项 指定文件夹 ls l a huangchao 7 ls 指令总结 二 mkd
  • Opencascade安装教程(Visual Studio 2017)

    之前尝试了一次Visual Studio 2019 Opencascade7 5 0的编译 编译成功了 但是在Qt中使用的时候一堆错误没有解决 加之之前的项目需要 所以卸载了VS2019 改安装了VS2017 如果不想找太多错误 不妨可以安
  • Python实现淘宝商品数据爬取——静态网页爬虫(仅供学习,切勿无限制爬取)

    一 关于淘宝网 淘宝网是亚太地区较大的网络零售 商圈 由阿里巴巴集团在2003年5月创立 淘宝网是中国深受欢迎的网购零售平台 拥有近5亿的注册用户数 每天有超过6000万的固定访客 同时每天的在线商品数已经超过了8亿件 平均每分钟售出4 8
  • modelsim do文件简介及仿真波形分析注意事项

    目录 前言 Modelsim指令介绍 步骤一 创建run wave do 步骤二 打开Modelsim 步骤三 do run wave do 步骤4 run sim bat 补充介绍 参考 前言 本文对 do文件进行整理介绍 并在后文引用
  • 简单四边形不等式优化dp(上)

    这里只讨论简单的二维四边形不等式优化dp 和简单的广义决策单调性板子 下文中 优于 一般指的是 不劣于 请自行分辨 四边形不等式 四边形不等式定义为 位于整数集合上的二元函数 f x y f
  • sklearn中主成分分析PCA参数解释

    主成分分析一般用于数据降维 在应用主成分分析包scikit learn时注意以下四点 1 用pca components 可以获取特征向量 且特征向量为行向量 例如W pca components 0 或W pca components 0
  • Python如何生成词云(详细分析)

    前言 今天教大家用wrodcloud模块来生成词云 我读取了一篇小说并生成了词云 先看一下效果图 效果图一 效果图二 根据效果图分析的还是比较准确的 小说中的主人公就是 程理 所以出现次数最多 图中有两种模式 一种是默认的模式 另一种是自己
  • 【SpringMvc】从Servlet的HttpServlet到SpringMVC的DispathServlet到Springboot的RequestMapping

    1 0 众所周知 一个http请求到我们服务器 web容器 tomcat jetty servlet 就会约定俗成去访问webapp路径下的web xml配置文件 首先读取的是两个节点 listener 和 context param 监听
  • 自动化测试-selenium+python3+HTMLTestRunner

    案例介绍 使用selenium框架测试并输出测试报告 一 准备工作 1 编辑器 pycharm 2 安装selenium first step second step 3 导入HTMLTestRunner 用来生成测试报告的 注意 pyth
  • 细数6种垃圾回收器的区别, 快进来看看有没有你要用的

    文章目录 前言 一 垃圾回收算法 1 复制算法 2 标记 清除算法 3 标记 整理算法 4 分代算法 二 Serial收集器 三 Parallel Scavenge收集器 四 ParNew收集器 五 CMS收集器 1 GC流程 2 CMS的