当前位置: 首页 > news >正文

JVM实战—5.G1垃圾回收器的原理和调优

大纲

1.G1垃圾回收器的工作原理

2.G1分代回收原理—性能为何比传统GC好

3.使用G1垃圾回收器时应如何设置参数

4.如何基于G1垃圾回收器优化性能

5.问题汇总

1.G1垃圾回收器的工作原理

(1)ParNew + CMS的组合有哪些痛点

(2)G1垃圾回收器

(3)G1如何实现垃圾回收的停顿时间是可控的

(4)Region可能属于新生代也可能属于老年代

(1)ParNew + CMS的组合有哪些痛点

Stop the World是最大的问题。无论是新生代GC还是老年代GC,都会或多或少产生STW现象,这对系统的运行是有一定影响的。

所以JVM对垃圾回收器的优化,都是朝减少STW的目标去做的。在这个基础之上,就诞生了G1垃圾回收器。G1垃圾回收器可以提供比ParNew + CMS组合更好的垃圾回收性能。

(2)G1垃圾回收器

G1垃圾回收器可以同时回收新生代和老年代的对象,不需要两个垃圾回收器配合起来运作,它自己就能搞定所有的垃圾回收。G1的一大特点就是把Java堆内存拆分为多个大小相等的Region。如下图示:

图片

然后G1也会有新生代和老年代,但是只是逻辑上的概念。也就是说,某些Region属于新生代,某些Reigon属于老年代。如下图示:

图片

G1的另一特点,就是可以设置每次垃圾回收时的最大停顿时间,以及指定在一个长度为M毫秒的时间片段内,垃圾回收时间不超N毫秒。

比如可指定,希望G1在垃圾回收时保证:在1小时内由G1垃圾回收导致系统停顿时间,不超过1分钟。

从前面的JVM优化思路可知,我们对内存合理分配,优化一些参数,就是为了尽可能减少YGC和FGC,尽量减少GC带来的系统停顿影响。

现在G1则可以直接指定在一个时间段内,垃圾回收导致的系统停顿时间不能超过多久。而G1会全权进行负责,保证达到这个目标,这样就相当于我们可以控制垃圾回收对系统性能的影响了。

(3)G1如何实现垃圾回收的停顿时间是可控的

如果G1要做到这一点,就必须要追踪每个Region里的回收价值。

什么是回收价值?即G1必须搞清楚每个Region里有多少垃圾对象。如果对一个Region进行垃圾回收,会耗费多长时间,可回收多少垃圾?

如下图示:G1通过追踪发现,1个Region中的垃圾对象有10M,回收它们要耗费1秒。另外一个Region中的垃圾对象有20M,回收他们需要耗费200毫秒。

图片

然后在GC时G1发现在最近一个时间段内,垃圾回收已导致几百毫秒的系统停顿。现在又要执行一次垃圾回收,那么对这些Region进行筛选后,发现必须回收上图中只需200ms就能回收20M的Region。如下图示:

图片

所以G1的核心设计是:G1可以让我们设定垃圾回收对系统的影响,G1会把内存拆分为大量的小Region,G1会追踪每个Region中可以回收的对象大小和预估时间,G1在垃圾回收时会尽量把垃圾回收对系统影响控制在指定时间范围内,同时在有限的时间内尽量回收尽可能多的垃圾对象。

(4)Region可能属于新生代也可能属于老年代

在G1中,每一个Region可能属于新生代,也可能属于老年代。刚开始一个Region可能谁都不属于,然后接着就被分配给了新生代。然后这个Region会被放入很多属于新生代的对象,接着触发了垃圾回收,需要回收这个Region。如下图示:

图片

然后下一次这个Region可能又被分配给了老年代,用来存放老年代需要长期存活的的对象。如下图示:

图片

所以在G1的内存模型中,一个Region会属于新生代也会属于老年代。于是就没有所谓新生代给多少内存,老年代给多少内存这一说法。新生代和老年代各自的内存区域是不停变动的,由G1自己去控制。

(5)总结

这里介绍了G1垃圾回收器的设计思想:包括Region划分、Region动态变成新生代或老年代,Region的按需分配。当触发G1垃圾回收时,可以根据设定的预期的系统停顿时间,来选择最少回收时间和最多回收对象的Region进行垃圾回收。保证GC对系统停顿的影响在可控范围内,同时尽可能回收最多对象。

接下来会介绍关于G1的更多技术细节,比如:

一.G1是如何工作的

二.对象什么时候进入新生代的Region

三.什么时候触发Region GC

四.什么时候对象进入老年代的Region

五.什么时候触发老年代的Region GC

2.G1分代回收原理—性能为何比传统GC好

(1)G1垃圾回收器的设计思想

(2)如何设定G1对应的内存大小

(3)新生代Region还会分Eden区和Survivor区

(4)G1的新生代垃圾回收

(5)对象什么时候进入老年代

(6)大对象Region

(7)总结

(1)G1垃圾回收器的设计思想

G1垃圾回收器设计的思想:就是把内存拆分为很多Region,然后新生代和老年代各自对应一些Region。回收的时候尽可能挑选停顿时间最短以及回收对象最多的Region,从而尽量保证达到指定的垃圾回收系统停顿时间。

(2)如何设定G1对应的内存大小

一.G1会把内存拆分为很多个Region内存区域,每个Region大小都一样

如下图示:

图片

二.每个Region的大小范围是1M~32M,而且必须是2的倍数

通过-Xms和-Xmx参数可以设置整个堆内存的大小,通过-XX:+UseG1GC参数可以指定使用G1垃圾回收器。

如果JVM启动时发现了指定使用G1垃圾回收器,那么默认情况下G1会自动用堆大小除以2048得出每个Region的大小。每个Region的大小范围是1M~32M,且必须是2的倍数。如果堆大小是4G = 4096M,除以2048,每个Region的大小就是2M。当然也可以通过-XX:G1HeapRegionSize参数来手动指定Region大小。

需要注意的是:按照默认值计算,G1可以管理的最大内存为2048 * 32M = 64G。假设设置xms=32G,xmx=128G。由于Region的大小最小是1M,最大是32M,而且要是2的倍数。那么初始化时按2048个Region计算,得出每个Region分区大小为32M。然后分区个数动态变化范围从1024个到4096个。

图片

系统刚开始运行时,默认新生代对堆内存的占比是5%。也就是占据200M左右的内存,对应大概是100个Region。这可以通过-XX:G1NewSizePercent来设置新生代初始占比,但通常维持默认值即可。

因为在系统运行中,JVM会不停地给新生代增加更多的Region。但新生代占比最多不超60%,可通过-XX:G1MaxNewSizePercent设置。而且一旦Region进行了垃圾回收,新生代的Region数量就会减少。

如下图示,系统刚开始运行时有一部分的Region是属于新生代的。

图片

(3)新生代Region还会分Eden区和Survivor区

G1虽然把内存划分为很多的Region,但还是有新生代、老年代的区分,而且新生代里同样有Eden和Survivor的划分。

所以前面介绍的很多原理在G1中都还是适用的。比如参数-XX:SurvivorRatio=8,系统刚开始运行时有100个Region。此时新生代中有80个Region是Eden区,20个Region是两个Survivor区。如下图示:

图片

所以在G1中还是有Eden和Survivor的,它们会占据不同数量的Region。然后随着对象不停地在新生代分配,属于新生代的Region会不断增加,Eden和Survivor对应的Region也会不断增加。

(4)G1的新生代垃圾回收

既然G1的新生代有Eden和Survivor之分,那么垃圾回收的机制也类似。当不停往新生代Eden的Region放对象,G1会不停给新生代加入Region。直到新生代占据堆大小的最大比例60%,一旦新生代大小达到了设定的占据堆内存大小的最大比例60%。比如2048个Region中有1200个Region都是属于新生代的了,里面的Eden占了1000个Region,每个Survivor占了100个Region,而且Eden中的Region都占满了对象。如下图示:

图片

这时就会触发新生代GC。G1就会使用复制算法来进行垃圾回收,进入Stop the World状态。然后把Eden对应的Region中的存活对象放入S1对应的Region中,接着回收掉Eden对应的Region中的垃圾对象,如下:

图片

G1的新生代垃圾回收过程和ParNew是有区别的。因为G1可以设定GC停顿时间,执行GC时最多会让系统停顿某个时间。可以通过-XX:MaxGCPauseMills参数来设定,默认值是200ms。G1会追踪每个Region,然后GC时根据回收各Region需要多少时间、以及可回收多少对象,来选择回收其中一部分Region。从而保证GC时的停顿时间控制在指定范围内,并尽可能多地去回收对象。

(5)对象什么时候进入老年代

在G1的内存模型下,新生代和老年代各自都会占据一定的Region。如果按照默认新生代最多只能占据堆内存2048个Region的60%的Region来推算,老年代最多可以占据40%的Region,大概就是800个左右的Region。

那么对象何时候会从新生代进入老年代?和ParNew几乎一样,还是以下几个条件:

一.对象在新生代躲过多次YGC,达到参数-XX:MaxTenuringThreshold设置的年龄

二.动态年龄判定规则,比如年龄为1岁、2岁、3岁、4岁的对象大小总和超过了Survivor的50%,此时Survivor区还有5岁+的对象,那么4岁及以上的对象就会全部进入老年代

三.新生代回收后存活的对象在Survivor区的Region都放不下了

所以经过一段时间的新生代使用和垃圾回收后,会有些对象进入老年代。如下图示:

图片

(6)大对象Region

一.G1内存模型下对大对象的分配策略

G1提供专门的Region存放大对象,不让大对象进入老年代的Region。G1中大对象的判定规则就是一个大对象超过了一个Region大小的50%。比如按照上面算的,每个Region是2M。那么只要一个大对象超过了1M,就会被放入大对象专门的Region中,而且一个大对象如果太大,可能会横跨多个Region来存放。如下图示:

图片

堆内存里哪些Region会用来存放大对象?60%的Region给新生代,40%的Region给老年代,那还有哪些Region给大对象?

其实在G1里,新生代和老年代的Region是不停的动态变化的。比如新生代现占1200个Region,但一次GC后里面1000个Region空了。此时这1000个Region就可以不属于新生代,可用部分Region放大对象,所以大对象既不属于新生代也不属于老年代。

二.G1内存模型下对大对象的回收策略

既然大对象既不属于新生代也不属于老年代,那何时会触发垃圾回收?

其实在新生代、老年代回收时,会顺带着大对象Region一起回收,这其实就是在G1内存模型下对大对象的分配和回收策略。

(7)总结

这里介绍了G1的内存模型和分配规则,包括:

一.每个Region多大(1-32M)

二.新生代包含多少Region(60%)

三.新生代动态增加Region(初始5% -> 60%)

四.G1中仍然存在Eden和Survivor两个区域

五.什么时候触发新生代的垃圾回收(新生代达到60%占比且满了)

六.G1新生代垃圾回收使用的复制算法

七.G1特有的预设GC停顿时间功能

八.对象进入老年代(15岁 + 动态年龄 + S区不足)

九.大对象的独立Region存放和回收

(8)问题

从新生代的垃圾回收来看,G1相比ParNew的优点:

一.停顿时间可以预设

二.大对象不再进入老年代

三.对象进入老年代的情况少很多

四.同样内存大小,Eden和Survivor都大很多

五.ParNew的GC需要停止系统程序,但G1的新生代GC可以不用停止

3.使用G1垃圾回收器时应如何设置参数

(1)G1的动态内存管理策略总结

(2)何时触发新生代 + 老年代的混合垃圾回收

(3)G1混合垃圾回收的过程

(4)G1垃圾回收器的一些参数

(5)回收失败时的Full GC

(1)G1的动态内存管理策略总结

G1的动态内存管理策略:根据情况动态地把Region分配给新生代(Eden+S区)、老年代和大对象。但是新生代和老年代会有一个各自的最大占比,新生代占比最大60%,老年代占比最大40%。然后在新生代的Eden满的时候,触发新生代垃圾回收。

G1新生代的垃圾回收还是采用了复制算法。只是会考虑预设GC停顿时间,保证垃圾回收的停顿时间不超预设时间。因此会挑选一些回收价值比较高的Region来进行垃圾回收。

然后G1新生代垃圾回收和ParNew一样:如果一些对象在新生代熬过一定次数GC,或触发了动态年龄判定规则,或GC后的存活对象在Survivor放不下,都会让对象进入老年代中。所以G1中的新生代对象还是会因为各种情况而慢慢地进入老年代的。

G1对大对象的处理则与ParNew不一样:G1的大对象会进入单独的大对象Region,不再进入老年代。

(2)何时触发新生代 + 老年代的混合垃圾回收

-XX:InitiatingHeapOccupancyPercent是G1的参数,默认值是45%。意思是如果老年代占据了堆内存的45%的Region时,就会尝试触发新生代 + 老年代一起回收的混合回收。

比如按照默认情况下的堆内存有2048个Region:如果老年代占据了其中45%的Region,就会开始触发混合回收。如下图示:

图片

(3)G1混合垃圾回收的过程

G1:初始标记-并发标记-最终标记-混合回收

CMS:初始标记-并发标记-重新标记-并发清除

一.首先进入初始标记阶段

这个阶段需要STW,标记GC  Roots直接引用的对象,这个过程速度是很快的。

如下图示:首先STW停止系统程序的运行。然后对各个线程栈内存中局部变量所代表的GC Roots,以及方法区中类静态变量所代表的GC Roots,进行扫描。也就是标记出这些GC Roots直接引用的对象。

图片

二.然后会进入并发标记阶段

这个阶段会允许系统程序的运行,同时进行GC Roots追踪,从GC Roots开始追踪所有的存活对象,如下:

图片

这里对GC Roots追踪进行说明,代码如下:

public class Kafka {    public static ReplicaManager replicaManager = new ReplicaManager();}
public class ReplicaManager {    public ReplicaFetcher replicaFetcher = new ReplicaFetcher();}

可以看到:Kafka类有一个静态变量是replicaManager,它就是一个GC Root对象。首先在初始标记阶段,仅仅会标记GC Roots直接引用的对象。所以会标记replicaManager作为GC Roots直接关联的对象,也就是表明堆内存中的ReplicaManager对象,它肯定是要存活的。

然后在并发标记阶段,就会进行GC Roots追踪。即会从replicaManager直接关联的ReplicaManager对象开始往下追踪,ReplicasManager对象里有一个实例变量replicaFetcher,此时追踪这个replicaFetcher变量可知它引用了ReplicaFetcher对象,于是这个ReplicaFetcher对象也要被标记为存活对象。

这个并发标记阶段还是很耗时的,因为要追踪全部的存活对象。但这个阶段可以跟系统程序并发运行,所以对系统程序影响不太大。而且在并发标记阶段对对象进行的修改,JVM也会记录起来。比如哪个对象被新建了,哪个对象失去了引用。

三.接着会进入最终标记阶段

这个阶段会STW禁止系统程序运行,但会根据并发标记时的记录,最终标记出哪些对象存活、哪些对象回收。如下图示:

图片

四.最后进入混合回收阶段

这个阶段首先会进行如下计算:老年代中各Region的存活对象数量、存活对象占比,还有执行垃圾回收的预期性能和效率。

接着会Stop The World停止系统程序,选择部分Region进行回收,因为必须让垃圾回收的停顿时间控制在指定的范围内。

比如老年代此时有1000个Region都满了:但是根据预定目标,本次垃圾回收可能只能停顿200毫秒。那么通过之前计算得知,可能回收其中800个Region刚好需要200ms。于是就只回收那800个Region,把GC停顿时间控制在指定范围内。如下图示:

图片

需要注意的是:老年代对堆内存占比达到45%时,触发的是混合回收。此时垃圾回收不仅会回收老年代,还会回收新生代,还会回收大对象。

那么到底会回收这些区域的哪些Region,那就要看情况了。因为G1为了满足设定的GC停顿时间要求,会从新生代、老年代、大对象里各自挑选一些Region,保证在指定的时间范围内(比如200ms)回收尽可能多的垃圾对象。这也就是所谓的混合回收,如下图示:

图片

(4)G1垃圾回收器的一些参数

在老年代的Region占据堆内存Region的45%之后,会触发混合回收。混合回收也就是Mixed GC,进行混合回收时会分为如下四个阶段:初始标记 -> 并发标记 -> 最终标记 -> 混合回收。在最后的混合回收阶段,会从新生代和老年代中都回收一些Region。

注意:G1会执行多次混合回收。即G1在最后的混合回收阶段时,会多次停止运行的系统程序。比如先停止系统运行,执行一次混合回收。回收掉一些Region后,恢复系统运行。然后再次停止系统运行,接着又执行一次混合回收。回收掉一些Region,恢复系统运行。

一.-XX:G1MixedGCCountTarget指定混合回收阶段会执行多少次回收

在一次MixedGC过程中,最后一个阶段应执行多少次回收,默认8次。为什么在最后一个混合回收阶段需要反复回收多次呢?因为停止系统一会儿,回收掉一些Region,再让系统运行一会儿。然后再次停止系统一会儿,再次回收掉一些Region。这样可以尽可能让系统的停顿时间不会太长,可以在多次回收的间隙,也运行一下程序。

二.-XX:G1HeapWastePercent指定结束混合回收时空Region的比例

G1在混合回收时,对Region的回收都是基于复制算法进行的。首先会把要回收的Region里的存活对象放入其他Region,然后清理原Region的对象,这样在回收过程中就会不断空出新的Region。一旦空出的Region达到默认堆内存大小的5%,那么此时就会结束本次的混合回收。

由于G1整体(新生代和老年代)是基于复制算法进行Region垃圾回收的,所以不会出现内存碎片的问题。G1不需要像CMS那样,在标记清理后再进行内存碎片的整理。

三.G1MixedGCLiveThresholdPercent指定被回收Region的存活对象占比

默认值是85%,意思是要回收的Region的存活对象大小占比要小于85%。如果一个Region中,其存活对象都占了该Region大小的85%以上。那么再把85%大小的存活对象都拷贝到另一个Region中的成本就会很高,所以就没必要回收这种Region了。

(5)回收失败时的Full GC

在进行Mixed GC回收时,新生代和老年代都是基于复制算法进行回收的。也就是Mixed GC会把要回收Region的存活对象拷贝到其他空闲的Region。如果在拷贝过程中发现没有空闲的Region可存放Mixed GC的存活对象了,那么就会触发一次Mixed GC失败时的Full GC。

一旦触发Mixed GC失败时的Full GC,就会停止系统程序。然后采用单线程进行标记、清理和压缩整理,清空出一批Region,这个过程会非常慢。

(6)问题

结合ParNew + CMS组合的JVM GC优化思路:

一.G1垃圾回收器中值得优化的地方(合理停顿 + 少MGC + 避免FGC )

二.什么情况可能会导致G1频繁触发Mixed GC(老年代占45%触发MGC)

三.如何减少MGC频率(S区足够大 +提高触发占比 + 不过早结束MGC)

4.如何基于G1垃圾回收器优化性能

(1)案例背景

(2)系统核心业务流程分析

(3)系统的运行压力

(4)在线教育系统背景总结

(5)G1垃圾回收器的默认内存布局

(6)GC停顿时间如何设置

(7)到底多长时间会触发新生代GC

(8)新生代GC如何优化

(9)Mixed GC如何优化

(1)案例背景

一个百万级注册用户的在线教育平台,主要目标用户群体是中小学生。注册用户大概是几百万,日活用户大概是几十万。

系统的业务流程也不复杂,普通用户浏览课程详情、下单付费、选课排课等低频行为几乎不用考虑。对于这样一个在线教育平台,其高频行为就是上课。

这个平台的使用人群是中小学生,该用户群体周一到周五白天要上学,放学后到八九点才会频繁使用平台,周末也会频繁地使用这个平台。

所以在每天晚上两三小时高峰期会有几十万日活用户来该教育平台上课,甚至可认为白天几乎没什么流量,而99%的流量都集中在晚上两三小时。

图片

(2)系统核心业务流程分析

接着来明确一下,用户在上课时主要高频使用的这个系统的哪些功能。假设用户使用该系统时,核心的业务流程就是游戏互动环节。通过游戏互动让用户感兴趣、愿意学、保持注意力、提升学习效果。

图片

也就是说,这个游戏互动功能,会承载用户高频率、大量的互动点击。比如在完成某任务时要点击很多按钮、频繁的进行互动。然后系统需要接收大量互动请求,并且记录用户的互动过程和互动结果。比如系统需要记录下用户完成了多少任务、做对了几个、做错了几个等。

(3)系统的运行压力

现在开始来分析一下这个系统运行时对内存使用产生的压力。核心就是在晚上两三小时高峰期内,每秒钟会有多少请求,每个请求会产生多少对象、占用多少内存,每个请求要处理多长时间。

一.首先估算晚上高峰期几十万用户使用系统时每秒会产生多少请求

假设晚上3小时高峰期内共有60万活跃用户,平均每个用户使用1小时。那么每小时会有20万活跃用户进行在线学习,这20万用户会进行大量互动操作。

假设一用户每分钟进行1次互动操作,那么一个用户一小时内就会进行60次互动操作,所以20万用户在1小时内会进行1200万次互动操作。平均到每秒大概就是3000次左右的互动操作,也就是系统每秒要处理3000并发请求。根据经验,一般需要部署5台4核8G机器,每台机器每秒处理600请求。这个压力可以接受,一般不会导致宕机的问题。

二.然后估算每个请求会产生多少个对象

一次互动请求不会有太复杂的对象,主要记录用户的一些互动过程。比如用户每完成一个活动,就给用户累加一些"XX币","XX宝石"等。所有一次互动请求大致会创建几个对象,占据几K的内存。一个对象大概几十个字段,每个Long字段8字节,一个对象就几百字节。加上系统其他功能的运行,一次请求假设涉及十几个这样的对象。那么一次请求涉及创建的对象占5K,一秒600请求就会占用3M内存。

(4)在线教育系统背景总结

在介绍百万用户在线教育平台的G1垃圾回收优化案例前,先分析了:系统核心业务、高峰压力、机器部署、每秒请求数、每秒内存压力。

接下来会基于每秒内存使用压力,结合G1的运行原理,进行如下分析:

G1垃圾回收机制会如何运行,在这个运行过程中可能会产生哪些问题;G1垃圾回收器在使用时有哪些地方是值得优化的;如何对G1的一些参数进行优化来调整垃圾回收性能;我们应该要合理分析系统的内存压力,然后合理优化JVM的参数,尽可能降低JVM GC的频率,同时降低JVM GC导致的系统停顿的时间。

(5)G1垃圾回收器的默认内存布局

系统采用了5台4核8G机器来部署,每台机器每秒会有600个请求占用3M的内存。假设给每台机器上的JVM分配了4G的堆内存,并且使用G1垃圾回收器。其中新生代默认初始占比为5%,最大占比为60%。每个Java线程的栈内存为1M,元数据区域(永久代)的内存为256M。此时JVM参数如下:

 -Xms4096M -Xmx4096M  -Xss1M  -XX:PermSize=256M -XX:MaxPermSize=256M -XX:+UseG1GC

-XX:G1NewSizePercent设置新生代初始占比,采用默认值5%。

-XX:G1MaxNewSizePercent设置新生代最大占比,采用默认值60%。

此时堆内存为4G,G1会除以2048,计算出每个Region的大小为2M。刚开始新生代只占5%的Region,即只有100个Region共200M内存空间。如下图示:

图片

(6)GC停顿时间如何设置

在G1垃圾回收器中有一个至关重要的参数会影响到GC的表现,就是-XX:MaxGCPauseMills,默认值是200毫秒。这个参数指定每次触发GC时导致的系统停顿时间期望不超过200毫秒,这个参数可以先保持默认值。

(7)到底多长时间会触发新生代GC

当系统运行起来后,会不停地在新生代的Eden区域内分配对象。按照前面的估算,每秒会分配3M大小的对象。如下图示:

图片

一.一些问题和假设

问题一:

Eden区空间不够,就触发新生代GC,那什么时候Eden区会内存不够?

问题二:

-XX:G1MaxNewSizePercent限定了新生代最多占用堆内存60%的大小。那么难道必须随着系统运行一直给新生代分配更多的Region,直到新生代占据60%的Region后,无法再分配Region才触发新生代GC?G1肯定不是这么做的。

二.G1的运行原理

假设在这个系统里,G1回收300个Region(600M内存)大概需要200ms。那么很有可能系统在运行时呈现出如下的效果:系统运行时每秒创建3M对象,大概1分钟就会塞满100个Region(200M)。如下图示:

图片

此时很可能G1会觉得:要是现在就触发一次新生代GC。那么回收区区200M只需要大概几十ms,最多就让系统停顿几十ms而已。这与启动时-XX:MaxGCPauseMills参数设定的200ms停顿时间相差甚远。所以要是现在就触发新生代GC,那么久可能会导致:回收完成后1分钟,再次占满新生代的这100个Region,又要触发新GC。这样每分钟都要执行一次新生代GC,过于频繁了,没这个必要。

因此G1可能就会觉得:还不如给新生代先增加一些Region。然后让系统继续运行着,在新增加的新生代Region中分配对象好了,这样就不用过于频繁的触发新生代GC。如下图示:

图片

然后系统继续运行,一直到可能300个Region都占满了。此时通过计算发现回收这300个Region大概需要200ms,那么可能这个时候才会触发一次新生代的GC。

由此可见,其实G1是很动态灵活的。它会根据设定的GC停顿时间给新生代不停分配更多Region。然后到一定程度,感觉差不多了,才会触发新生代GC。从而保证新生代GC时导致的系统停顿时间在预设范围内,而且也避免了频繁的新生代GC。

需要注意的是:

分配多少Region给新生代、多久触发一次新生代GC、每次耗费多长时间。G1并不能确定,必须通过工具查看系统实际情况才知道,无法提前预知。

G1的运行原理总结:

G1会根据预设的GC停顿时间,给新生代分配一些Region。然后到一定程度才触发GC,并且把GC停顿时间控制在预设范围内,尽量避免一次性回收过多Region导致GC停顿时间超出预期。

(8)新生代GC如何优化

垃圾回收器是一代比一代先进的,虽然内部实现机制越来越复杂,但是优化却越来越简单。

比如对于G1而言:

一.首先给整个JVM的堆区域足够的内存

比如我们在这里就给了JVM超过5G的内存,其中堆内存有4G的内存。

二.接着合理设置-XX:MaxGCPauseMills参数

如果这个参数设置太小了:

那么说明每次GC停顿时间可能特别短。此时G1可能在发现几十个Region占满时,就要开始触发新生代GC。从而导致新生代GC频率特别频繁。比如如果设置每次停顿30毫秒,那么可能会每30秒触发一次新生代GC。

如果这个参数设置过大了:

那么G1会允许不停地在新生代分配新对象。然后积累很多对象,再一次性回收几百个Region。此时可能一次GC停顿时间就会达到几百毫秒,但GC的频率很低。比如每30分触发一次新生代GC,但每次停顿500毫秒。

所以预期的GC停顿时间到底如何设置,需要结合系统压测工具、GC日志、内存分析工具来考虑,尽量别让系统的GC频率太高,同时每次GC停顿时间也别太长。

(9)Mixed GC如何优化

一.频繁触发Mixed GC的关键

新生代对象进入老年代的几个条件是:YGC后存活对象太多没法放入Survivor区 + 对象年龄太大 + 动态年龄判定规则。

Mixed GC的触发条件是:老年代在堆内存里占比超过45%。

在新生代对象进入老年代的几个条件其中比较关键的就是:新生代GC后存活对象太多无法放入Survivor区和动态年龄判定规则,因为这两个条件可能让很多对象快速进入老年代。一旦老年代达到占用堆内存45%的阈值,那么就会频繁触发Mixed GC。

所以Mixed GC本身很复杂,很多参数可以优化。但是优化Mixed GC的核心不是优化它的参数,而是和前面分析的一样。尽量避免对象过快进入老年代,避免频繁触发Mixed GC,就能实现优化。

二.合理设置-XX:MaxGCPauseMills避免频繁触发Mixed GC

由于G1和ParNew + CMS的组合是不同的,那应该如何来优化参数呢?其实核心的还是-XX:MaxGCPauseMills这个参数。

如果-XX:MaxGCPauseMills参数设置的值很大,导致系统运行很久,新生代都占用堆内存的60%时才触发新生代GC。那么存活下来的对象可能就会很多,导致Survivor区放不下那么多对象。于是这些存活下来的对象就会全部进入老年代,或者存活下来的对象比较多,达到S区的50%,触发动态年龄判定规则,那么也会导致下一次新生代GC的存活对象快速进入老年代。

所以核心还是在于调节-XX:MaxGCPauseMills这个参数的值。在保证新生代GC不太频繁的同时,还得考虑每次GC后有多少存活对象。避免存活对象太多快速进入老年代,频繁触发Mixed GC。

5.问题汇总

问题一:

一个广告系统,使用的就是G1垃圾回收器。因为堆内存有30G,传统回收器可能会造成很大的停顿,所以使用了G1。

答:G1非常适合超大内存的机器。因为内存太大,不用G1会导致新生代每次GC回收垃圾太多,停顿太长。使用G1则可以指定每次GC停顿时间,每次回收一部分Region。

问题二:

从GC效果上看,G1最明显的特点就是可以预测STW的时间。G1为了达到这个效果,抛弃传统分代内存,分成各个小内存块Region。针对这些Region计算垃圾回收价值,然后选某些性价比高的进行GC,以便在预先设定的GC时间内完成GC。所以是不是G1可以用在对STW特别敏感的业务上?比如实时通信等追求低延迟响应的业务。

答:是的。还有就是那种大内存机器,比如16G,32G的机器部署的系统。大内存机器如果不用G1,那么新生代满时对象太多,一次GC时间太长。而用了G1则可以控制停顿时间,每次只回收部分Region即可。

问题三:

G1按Region回收会不会形成新的内存碎片?

答:不会。Region回收时使用的是复制算法,会将存活对象拷贝到其他Region。然后再对原来的Region直接回收掉全部垃圾。

问题四:

G1分那么多Region,有点像HDFS里的小文件,小文件太多会影响性能。但是为什么G1的性能会比之前那些更好?

答:划分为很多的Region,回收时按照设定只能停顿系统20ms。所以就会挑选少量Region来回收,这样可以控制垃圾回收的停顿时间。如果按照ParNew + CMS组合简单分代划分,必须回收整个新生代。这时每次GC回收的内存区域大了,必然要停顿更久时间。

问题五:

一个Spring Boot应用在8G内存开发机上跑,启动需要加载的类特别多。每次JVM一启动,新生代就以每秒10M的速度增长,光启动就要十分钟。因为发现启动期间就进行了两次Full GC,半小时执行了十几次YGC。于是就调整了新老比例为2比1,共分配4G。之后FGC一直为0, YGC半小时只有两次,启动时间也降为1分钟以内。

答:是的,这就是典型的新生代内存不足导致的。系统启动时要创建一堆对象,发现新生代不够。于是频繁YGC,很多对象进入到老年代。然后老年代又不足,又要对老年代Full GC。最后就出现十多次YGC + 几次Full GC。

由于GC太多会导致系统启动速度很慢。优化比例后,新生代内存充足,很多对象直接进入新生代不用进老年代。于是最多就是少数YGC回收一部分对象,也不会有FGC。GC次数减少了,那系统启动速度也就快了。

问题六:

G1垃圾回收器也应该合理分配新生代的占比,保证S区足够大。不让存活对象很快进入老年代,不让老年代很快占到45%。如果老年代不那么快占到45%,自然就可以减少混合回收。

问题七:

一.G1混合回收在第四个阶段会进行多次混合回收,这个多次混合回收的间隔是由G1自己控制的。

二.空闲的Region数量达到堆内存5%就会停止回收,即默认最多进行8次混合回收。但可能到了4次,发现空闲Region达到5%就不进行混合回收了。

三.Mixed GC回收失败时Full GC,应该是采用Serial Old回收器。

问题八:

使用G1垃圾回收器时,值得优化的地方。

对于新生代:

目标是避免短期存活的对象进入老年代。

一.预估系统每次Young GC后存活对象,确保Survivor区能放得下

二.高峰期避免满足动态年龄判断条件,导致短期存活对象进入老年代

三.大对象有大对象的Region,不占用老年代空间,基本不用考虑

对于老年代:

一.合理设置预测的停顿时间,并非越小越好

如果过小,有可能多次回收效果不大,导致频繁新生代GC,很多短期对象进入老年代。然后老年代很快达到45%触发MGC,可能出现回收失败导致FGC。

二.适当提高-XX:G1HeapWastePercent比例

避免万一真的遇到了高峰期,短期存活对象进入老年代。但是回收时,进行了几次混合回收就刚好达到了5%。但是在老年代Region中可能还存在某些短期存活对象没有被回收,避免过早结束混合回收,导致频繁新生代GC。

三.-XX:G1MixedGCLiveThresholdPercent可忽略

降低这个回收Region的垃圾对象占比可能会让Region的回收效率更高,但也可能导致短期存活对象驻留内存时间过长,进入老年代的风险。

问题九:

(1)使用G1垃圾回收时,值得优化的地方

JVM优化要让短命对象在新生代回收,长期存活对象尽早进入老年代,G1的优化思路亦是如此。

需要根据具体业务系统:首先合理分配老年代和新生代大小、新生代Eden区和Survivor区大小。其次合理设置G1的MaxGCPauseMills大小。MaxGCPauseMills太小容易造成GC频繁,影响系统的吞吐量。MaxGCPauseMills太大会增大系统的停顿时间,影响用户体验。

(2)什么时候会导致G1频繁触发Mixed混合垃圾回收

一.InitiatingHeapOccupancyPercent设置太小

二.新生代和老年代空间设置不合理,导致进入老年代对象太多,从而频繁达到MixedGC的条件

(3)如何尽量减少Mixed GC的频率

触发MixedGC条件:老年代达到InitiatingHeapOccupancyPercent的值。

一.让垃圾对象尽量在新生代就被回收掉,尽量让短命对象不进老年代

这就要根据具体业务系统来合理设置新生代Eden大小和Survivor的大小。

二.将老年代设置较小的值或提高参数InitiatingHeapOccupancyPercent的值

这样就可以使触发MixedGC概率降低,但这样也可能会存在一些问题。

设置老年代内存为较小的值存在的问题:

如果有较多的需要长期存活的对象的情况下,容易FGC或直接OOM了。

提高InitiatingHeapOccupancyPercent的问题:

虽然降低了MixedGC的频率,但导致老年代存在过多的对象。这样每次进行老年代回收时,就会增加:"并发标记阶段"的计算负担和"混合回收阶段"计算和预估的负担,也就是不太适合CPU负载较高的计算型业务的系统。

问题十:

公司业务系统使用G1分析:比如机器堆内存5G,新生代最多占堆内存的60%,即3G。1秒产生3M对象,1分就产生180M对象。

Eden区和Survivor区按默认的8 : 1 : 1比例来计算。Eden区有2400M,占满需要14分,故每14分就会进行一次新生代回收。每个Survivor区有300M内存,每次新生代回收有180M对象会存活下来。此时超过S区50%,所以每次新生代回收存活的180M对象会进入老年代。

也就是每隔14分钟就会有180M的垃圾进入老年代,这样一个小时大概有800M会进入老年代。老年代达到45%时,即2G * 45% = 900M,会进行混合回收。大概1个小时零10分后,会进行一次混合回收。

这个系统其实还可以,系统性能不会太差。

问题十一:

传统的垃圾收集器是通过调整Eden区和Survivor区的大小来控制。而G1则比较先进,直接指定一个期望的停顿时间。选择停顿时间的标准是:既不能导致频繁触发YGC,也不能导致一次回收过多对象停顿时间过长。

所以还得通过工具来调试出一个最适合自己系统的停顿时间,通过工具检测,要得出一个停顿多久可以回收多少的内存大小的指标。再根据这个指标和业务系统生成垃圾的速率设置合理的停顿时间。

问题十二:

G1这种垃圾回收器到底在什么场景下适用?哪些场景适合采用ParNew + CMS垃圾回收器?

答:一.G1压缩内存空间会比较有优势,适合产生大量碎片的应用。

二.G1能够设置可以预期的GC停顿时间,对低延时应用更有优势。

三.其他垃圾收集器对大内存回收耗时较长;G1对内存分成多块区域,能根据预期停顿时间选性价比高的区域来回收,G1适用JVM内存占用大的应用。

四.ParNew + CMS回收器适用内存小、对象在新生代中存活短的应用。比如贷款业务,申请额度后就在后台处理了,有额度以后再通知用户。

问题十三:

使用系统的用户其实并不关心什么GC频率,但关心的是使用的系统卡不卡,处理速度快不快。系统卡不卡会受-XX:MaxGCPauseMills影响,系统处理速度快不快会受GC的频率影响。

GC的频率高,对应的应该是系统的吞吐量低。GC频率高说明用来处理垃圾回收的时间变多,处理业务的时间变少。比如原先每秒可以取出1万条数据,现在只能取出2千条。

相关文章:

JVM实战—5.G1垃圾回收器的原理和调优

大纲 1.G1垃圾回收器的工作原理 2.G1分代回收原理—性能为何比传统GC好 3.使用G1垃圾回收器时应如何设置参数 4.如何基于G1垃圾回收器优化性能 5.问题汇总 1.G1垃圾回收器的工作原理 (1)ParNew CMS的组合有哪些痛点 (2)G1垃圾回收器 (3)G1如何实现垃圾回收的停顿时间是…...

Python爬虫(selenium)从网站获取信息并存入数据库(mysql)

简介: 在本篇博客中,我们将介绍如何使用Python编写一个简单的网络爬虫,从指定网站上获取图书信息,并将这些信息存入数据库。这个项目涉及到Python编程、selenium爬虫技术以及数据库操作等内容,适合对这些领域感兴趣的初…...

spring中使用@Validated,什么是JSR 303数据校验,spring boot中怎么使用数据校验

文章目录 一、JSR 303后台数据校验1.1 什么是 JSR303?1.2 为什么使用 JSR 303? 二、Spring Boot 中使用数据校验2.1 基本注解校验2.1.1 使用步骤2.1.2 举例Valid注解全局统一异常处理 2.2 分组校验2.2.1 使用步骤2.2.2 举例Validated注解Validated和Vali…...

RabbitMQ中的异步Confirm模式:提升消息可靠性的利器

在现代分布式系统中,消息队列(Message Queue)扮演着至关重要的角色,它能够解耦系统组件、提高系统的可扩展性和可靠性。RabbitMQ作为一款广泛使用的消息队列中间件,提供了多种机制来确保消息的可靠传递。其中&#xff…...

C++ 设计模式:代理模式(Proxy Pattern)

链接:C 设计模式 链接:C 设计模式 - 门面模式 链接:C 设计模式 - 中介者 链接:C 设计模式 - 适配器 代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制&#xff08…...

41.1 预聚合提速实战项目之需求分析和架构设计

本节重点介绍 : 需求分析架构设计 需求分析 使用预聚合提速查询并且降低高基数查询对后端的压力用户无需变更grafana上的查询语句,后端自动替换效果图 架构设计 架构图 解决方案说明 heavy_query对用户侧表现为查询速度慢在服务端会导致资源占用过多甚至打挂…...

学习路之VScode--自定义按键写注释(插件)

1. 安装 "KoroFileHeader" 插件 首先,在 VScode 中搜索并安装名为 "KoroFileHeader" 的插件。你可以通过在扩展商店中搜索插件名称来找到并安装它。 2. 进入 VScode 设置页面 点击 VScode 左下角的设置图标,然后选择 "设置&q…...

Web安全 - “Referrer Policy“ Security 头值不安全

文章目录 概述原因分析风险说明Referrer-Policy 头配置选项1. 不安全的策略no-referrer-when-downgradeunsafe-url 2. 安全的策略no-referreroriginorigin-when-cross-originsame-originstrict-originstrict-origin-when-cross-origin 推荐配置Nginx 配置示例 在 Nginx 中配置 …...

Milvus×EasyAi:如何用java从零搭建人脸识别应用

如何从零搭建一个人脸识别应用?不妨试试原生Java人工智能算法:EasyAi Milvus 的组合拳。 本文将使用到的软件和工具包括: EasyAi:人脸特征向量提取Milvus:向量数据库用于高效存储和检索数据。 01. EasyAi:…...

CGAL windows 安装教程

1.下载源代码 CGAL官网下载https://github.com/CGAL/cgal/releases 2.下载boost库 BOOST官网下载https://www.boost.org/ 3.下载 GMP and MPFR 4.配置VS2022 头文件: 库路径 做完以上步骤,可以使用CGAL了!...

低代码开源项目Joget的研究——Joget8社区版安装部署

大纲 环境准备安装必要软件配置Java配置JAVA_HOME配置Java软链安装三方库 获取源码配置MySql数据库创建用户创建数据库导入初始数据 配置数据库连接配置sessionFactory(非必须,如果后续保存再配置)编译下载tomcat启动下载aspectjweaver移动jw…...

【Ubuntu 20.4安装截图软件 flameshot 】

步骤一: 安装命令: sudo apt-get install flameshot 步骤二: 设置快捷方式: Ubuntu20.4 设置菜单,点击 号 步骤三: 输入软件名称, 软件快捷命令(flameshot gui)&am…...

AUTOSAR 平台介绍 (R24-11新标准发布!)

AUTOSAR 平台介绍——R24-11 0引言 随着技术的不断进步和市场需求的变化,AUTOSAR在汽车行业中的重要性日益增强。R24-11版本作为AUTOSAR平台的重要更新,旨在提升系统性能、安全性和用户体验。本文将详细介绍R24-11版本的主要更新内容 1 AUTOSAR介绍 AUTOSAR汽车软件架构,旨…...

Framework开发入门(一)之源码下载

一、使用Linux操作系统的小伙伴可以跳转到官网链接按提示操作 官网源码地址:下载源代码 | Android Open Source Project 1.创建一个空目录来存放您的工作文件。为其指定一个您喜欢的任意名称: mkdir WORKING_DIRECTORYcdWORKING_DIRECTORY …...

CentOS7下的 OpenSSH 服务器和客户端

目录 1. 在 IP 地址为 192.168.98.11 的 Linux 主机上安装 OpenSSH 服务器; 2. 激活 OpenSSH 服务,并设置开机启动; 3. 在 IP 地址为 192.168.98.22 的 Linux 主机上安装 OpenSSH 客户端,使用客户端命令(ssh、 scp、…...

rocketmq5--(三)--broker发送消息给消费者

202412/30回过头来记录一下:之前一直找不到在哪里把消息丢给消费者(2024/11/23),找了很久都没找到,就放弃了,然后发现,我靠原来是在poplongpollingservice里。。。。还是乱打断点,偶…...

QComboBox中使用树形控件进行选择

事情是这样的,要在一个ComboBox中通过树形结构进行内容的选择。 默认的QComboBox展开是下拉的列表。因此需要定制一下。 效果就是这样的 实现上面效果的核心代码就是下面这样的 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { treenew…...

单片机中运行多个定时器

在单片机的裸机编程环境中,同时运行多个定时器是完全可行的,但需要注意一些关键点以确保系统的稳定性和效率。以下是一些考虑因素和实现方法: 1. 硬件支持 定时器数量:首先确认您的单片机是否具备足够的定时器资源。大多数现代…...

go 模拟TCP粘包和拆包,及解决方法

1. 什么是 TCP 粘包与拆包? 粘包(Sticky Packet) 粘包是指在发送多个小的数据包时,接收端会将这些数据包合并成一个数据包接收。由于 TCP 是面向流的协议,它并不会在每次数据发送时附加边界信息。所以当多个数据包按顺…...

论文笔记PhotoReg: Photometrically Registering 3D Gaussian Splatting Models

1.abstract 最近推出的3D高斯飞溅(3DGS),它用多达数百万个原始椭球体来描述场景,可以实时渲染。3DGS迅速声名鹊起。然而,一个关键的悬而未决的问题仍然存在:我们如何将多个3DG融合到一个连贯的模型中?解决这个问题将使…...

宝塔服务器安装备份配置

1.服务器下载安装宝塔功能 if [ -f /usr/bin/curl ];then curl -sSO https://download.bt.cn/install/install_panel.sh;else wget -O install_panel.sh https://download.bt.cn/install/install_panel.sh;fi;bash install_panel.sh ed8484bec执行后选择y 等待下载完成会给出…...

ubuntu22 安装CUDA

在Ubuntu系统中,使用nvidia-smi命令可以看到当前GPU信息,在右上角可以看到CUDA Version,意思是最大支持的CUDA版本号。 安装下载 CUDA Toolkit 11.6 Downloads | NVIDIA Developer https://developer.nvidia.com/cuda-downloads?target_osL…...

LabVIEW故障诊断中的无故障数据怎么办

在使用LabVIEW进行故障诊断时,可能会面临“无故障数据”的情况。这种情况下,缺乏明确的故障参考,使得系统难以通过传统对比法进行故障识别。本文将介绍应对无故障数据的关键策略,包括数据模拟、特征提取和基于机器学习的方法&…...

开发模式选择与最佳实践指南20241230

开发模式选择与最佳实践指南 引言 在现代软件开发中,选择合适的开发模式直接影响项目的开发效率和质量。本文将帮助您: 🎯 了解三种主流开发模式的优缺点💡 根据项目特点选择最适合的开发模式🔧 掌握混合开发模式的…...

超详细!一文搞定PID!嵌入式STM32-PID位置环和速度环

本文目录 一、知识点1. PID是什么?2. 积分限幅--用于限制无限累加的积分项3. 输出值限幅--用于任何pid的输出4. PID工程 二、各类PID1. 位置式PID(用于位置环)(1)公式(2)代码使用代码 2. 增量式…...

Redhat7 PCS建立无共享存储浮动地址集群

更新记录 日期版本号内容9/22/2024Ver 1.0重新排版修正 0写在前面 0.1 简述 时间有限使用VMware6.7环境使用Centos7.8最小化安装方式(不用配置本地yum仓库)注意查看主机名(主机双机操作,部分单机操作) 序号HostIPAd…...

爱思唯尔word模板

爱思唯尔word模板 有时候并不一定非得latex https://download.csdn.net/download/qq_38998213/90199214 参考文献书签链接...

交换机Vlan中 tagged和untagged的区别

pvid,tagged与untagged pvid是交换机一个端口上的id,一个端口只能有一个pvid,多个端口可以有相同的pvid。 一:接收数据 Untagged:不管收到的数据帧是否已经有VLAN标记,将数据帧中的vlan标记修改为自己的pvi…...

软件需求分析期末知识点整理

前言:本文为wk学子量身打造,帮助大家少挂科。主要根据ls的会议进行整理。懂得都懂。 重点还是多看看课本 第2章 需求获取的方法 第3章 3.1.2 控制需求(案例*2) 第4章 4.3 范式 第5章 5.2.3 原子功能(案例) 5.2.4 划分功能(案例)5.3.3 工作流图(画图) 第…...

PyAudio使用手册

PyAudio 是一个功能强大的 Python 库,用于在 Python 中进行音频输入和输出操作 1. 安装 在使用 PyAudio 之前,需要先安装它。可以使用 pip 进行安装: pip install pyaudio在某些系统(如 Ubuntu)上,可能还需…...

总结TCP/IP四层模型

总结TCP/IP四层模型 阅读目录(Content) 一、TCP/IP参考模型概述 1.1、TCP/IP参考模型的层次结构二、TCP/IP四层功能概述 2.1、主机到网络层  2.2、网络互连层  2.3、传输层  2.3、应用层 三、TCP/IP报文格式 3.1、IP报文格式3.2、TCP数据段格式3.3、UDP数据段格式3.4、套…...

《深入挖掘Python加解密:自定义加密算法的设计与实现》

利用python实现加解密 在正式编写各种加解密前,我们先写个小案例,如下。 封面在文末呦! 基础加解密-源码 # 加密 def encode():source01 乐茵for c in source01:ascii01 ord(c)ascii01 1print(chr(ascii01), end)# 解密 def decode():…...

【前端,TypeScript】TypeScript速成(六):函数

函数 函数的定义 定义一个最简单的加法函数: function add(a: number, b: number): number {return a b }(可以看到 JavaScript/TypeScript 的语法与 Golang 也非常的相似) 调用该函数: console.log(add(2, 3)) // out [LOG…...

Python中元组(tuple)内置的数据类型

在Python中,元组(tuple)是一种内置的数据类型,用于存储不可变的有序元素集合。元组在很多方面与列表(list)相似,但它们之间存在一些关键的区别。以下是关于Python元组的详细解释: 定…...

AI安全的挑战:如何让人工智能变得更加可信

引言 随着人工智能(AI)技术在各个领域的广泛应用,尤其是在医疗、金融、自动驾驶和智能制造等行业,AI正在重塑我们的工作和生活方式。从提高生产效率到实现个性化服务,AI带来了前所未有的便利。然而,在享受这…...

redis用途都有哪些

Redis,作为一个开源的高性能键值对数据库,其用途广泛且功能强大。 1. 缓存(Caching): • Redis常被用作缓存层,存储那些频繁访问但不易改变的数据,如用户会话、商品详情等。 • 通过将这些数据存…...

【Django篇】--动手实现路由模块化与路由反转

一、路由模块化 在一个Django项目中,由于功能类别不同,因此需要将不同功能进行模块化设计。在Django项目中模块化设计则需要将不同模块封装为对应的app模块,每一个模块中涉及到的路由则也需要进行模块化设计,才能更好的让整个项目…...

自研国产零依赖前端UI框架实战008 用户表单以及随机ID

前言 通过前面的努力,我们的组件已经越来越多了,我们的功能也越来越完善. 不过我们的新增用户的功能还没有做. 接下来, 就让我们实现新增用户的功能. 显示新增用户的表单 首先, 我们先把新增用户的表单显示出来. 我们可以复用之前的组件. <zdp_button1 text"新增…...

【数据结构-单调队列】力扣LCR 184. 设计自助结算系统

请设计一个自助结账系统&#xff0c;该系统需要通过一个队列来模拟顾客通过购物车的结算过程&#xff0c;需要实现的功能有&#xff1a; get_max()&#xff1a;获取结算商品中的最高价格&#xff0c;如果队列为空&#xff0c;则返回 -1 add(value)&#xff1a;将价格为 value …...

项目管理和协作平台Maintainer、Guest、Reporter、Owner 和 Developer 是常见的用户角色

在项目管理和协作平台上&#xff0c;Maintainer、Guest、Reporter、Owner 和 Developer 是常见的用户角色&#xff0c;每个角色有不同的权限和责任。以下是这些角色的详细区别&#xff1a; 1. Guest&#xff08;访客&#xff09; 权限&#xff1a;最低级别的权限。访问&#…...

探索电商数据:爬取不同平台商品信息的Python实践

在数字化时代&#xff0c;电商平台的商品信息成为了宝贵的数据资源。除了亚马逊&#xff0c;全球还有许多电商平台的商品信息值得爬取。本文将介绍几个值得关注的电商平台&#xff0c;并提供Python代码示例&#xff0c;展示如何爬取这些平台的商品信息。 1. 京东 (JD.com) 京…...

Autoware Universe 安装记录

前提&#xff1a; ubuntu20.04&#xff0c;英伟达显卡。 演示&#xff1a;https://www.bilibili.com/video/BV1z4CbYFEwr/?spm_id_from333.337.search-card.all.click ROS2-Galactic安装 wget http://fishros.com/install -O fishros && . fishros 选择galactic(R…...

CAT3D: Create Anything in 3D with Multi-View Diffusion Models 论文解读

24年5月的论文&#xff0c;上一版就是ReconFusion 目录 一、概述 二、相关工作 1、2D先验 2、相机条件下的2D先验 3、多视角先验 4、视频先验 5、前馈方法 三、Method 1、多视角扩散模型 2、新视角生成 3、3D重建 一、概述 该论文提出一种CAT3D方法&#xff0c;实现…...

群落生态学研究进展▌Hmsc包对于群落生态学假说的解读、Hmsc包开展单物种和多物种分析的技术细节及Hmsc包的实际应用

HMSC&#xff08;Hierarchical Species Distribution Models&#xff09;是一种用于预测物种分布的统计模型。它在群落生态学中的应用广泛&#xff0c;可以帮助科学家研究物种在不同环境条件下的分布规律&#xff0c;以及预测物种在未来环境变化下的潜在分布范围。 举例来说&a…...

C 进阶 — 程序环境和预处理

C 进阶 — 程序环境和预处理 主要内容 程序的编译和执行环境 C 程序编译和链接 预定义符号 预处理指令 #define 预处理指令 #include 预处理指令 #undef 预处理操作符 # 和 ## 宏和函数对比 命令行定义 条件编译 一 程序的编译和执行环境 ANSI C 存在两个不同环境…...

基于单片机的温湿度采集系统(论文+源码)

2.1系统的功能 本系统的研制主要包括以下几项功能&#xff1a; (1)温度检测功能&#xff1a;对所处环境的温度进行检测&#xff1b; (2)湿度检测功能&#xff1a;对所处环境的湿度进行检测&#xff1b; (3)加热和制冷功能&#xff1a;可以完成加热和制冷功能。 (4)加湿和除…...

【数据分析处理之缺失值】

文章目录 一、缺失值的影响1. 统计分析的偏差2. 机器学习模型的性能下降3. 数据质量和可信度下降4. 数据利用率降低5. 增加数据预处理的复杂度 二、识别缺失值1. 使用工具识别缺失值2. 可视化缺失数据 三、处理缺失值的策略1. 删除含缺失值的行或列2. 填充缺失值a. 用常数填充b…...

【大模型实战篇】Mac本地部署RAGFlow的踩坑史

1. 题外话 最近一篇文章还是在11月30日写的&#xff0c;好长时间没有打卡了。最近工作上的事情特别多&#xff0c;主要聚焦在大模型的预训练、微调和RAG两个方面。主要用到的框架是Megatron-DeepSpeed&#xff0c;后续会带来一些分享。今天的文章主要聚焦在RAG。 近期调研了一系…...

SQL Server实现将分组的其他字段数据拼接成一条数据

在 SQL Server 中&#xff0c;可以使用 STRING_AGG 函数&#xff08;SQL Server 2017 及更高版本支持&#xff09;将分组的其他字段数据拼接成一条数据。以下是示例代码&#xff1a; 假设有一个表 Orders&#xff0c;结构如下&#xff1a; OrderIDCustomerIDProduct1C001Appl…...

STM32 高级 物联网通讯之蓝牙通讯

目录 蓝牙基础知识 蓝牙概述 蓝牙产生背景 蓝牙发展历程 蓝牙技术类型 经典蓝牙(BR/EDR和AMP) 低功耗蓝牙(BLE) 市场上常见蓝牙架构 SOC蓝牙单芯片方案 SOC蓝牙+MCU方案 蓝牙host+controller分开方案 蓝牙协议栈 蓝牙芯片架构 BLE低功耗蓝牙协议栈框架 物理…...