G1原理—10.如何优化G1中的FGC
大纲
1.G1的FGC可以优化的点
2.一个bug导致的FGC(Kafka发送重试 + subList导致List越来越大)
3.为什么G1的FGC比ParNew + CMS要更严重
4.FGC的一些参数及优化思路
1.G1的FGC可以优化的点
(1)FGC的基本原理
(2)遇到FGC应该怎么处理
(3)应该如何操作来规避FGC
(4)应该如何操作来加快FGC的速度
(1)FGC的基本原理
一.FGC的并行处理
G1有两个得天独厚的优势:
优势一.Region是一个相对独立的内存区域
优势二.每个Region都有一个RSet
通过GC Roots + RSet,就能完整对某Region进行所有存活对象的标记。
二.FGC的并行处理流程
并行FGC开始前的前置工作:对象头、锁信息等信息的保存处理。在保存完一些对象头相关的信息之后,就要开始FGC了。
具体步骤和串行化的FGC是类似的。
步骤一:标记所有存活对象
步骤二:计算对象的新地址
步骤三:更新对象间的引用地址
步骤四:移动对象完成压缩
步骤五:对象移动后的后续处理
三.并行标记过程STW
FGC并行化后,其并行标记过程和串行化过程时差别不是很大,都是要标记出来所有的存活的对象。
需要注意的是:因为是并行化处理,所以多个线程在进行并行标记时,每个线程都会比起串行化处理时,多一个标记栈(任务栈)。也就是把起始对象GC Roots分成多份,每一个GC线程持有一部分。
FGC会对所有堆分区里的对象都进行标记,而且系统程序会STW。
四.FGC标记过程中的任务窃取
完成任务栈的对象标记的线程会从未完成的线程那里窃取一些任务。
(2)遇到FGC应该怎么处理
一.尽可能避免
FGC是需要极力避免的,JVM的优化手段多数都是尽可能减少FGC的出现。比如调整分代比例、Region大小、停顿时间、老年代预留空间比例等。所以,对于FGC的处理,最重要的手段就是避免它。
二.尝试优化FGC的速率
如果是正常的FGC,优化速率的方法就是,减少FGC需要处理的量。也就是FGC时,避免堆中存在大量需要复杂处理的对象。
(3)应该如何操作来规避FGC
基本的思路还是要避免达到产生FGC的条件。
一.产生FGC的条件主要是以下两种
条件一:MGC不及时,导致垃圾对象存活过多,造成空间不够
这其实也是并发标记的启动时机存在问题。如果并发标记启动的频率,远远落后于垃圾产生的速率。那么就会出现大量空间被垃圾对象占用,导致不必要的FGC。另外就是回收速度太低,导致停顿时间内回收垃圾太少,造成空间不够。
条件二:存活对象太多,各种GC都尝试过,无法腾出足够空间给新对象
执行了YGC + MGC后出现晋升失败,不得不进行FGC。
二.产生FGC的场景主要是以下两种
场景一:G1使用的算法,是标记复制算法(YGC和MGC) + 标记整理算法(FGC)。在进行垃圾回收时,新创建的对象及存活的对象,没有足够空间可使用。复制操作无法实现,因为每次GC的存活对象要复制到空闲Region中。
场景二:多次GC后仍然无法给新对象腾出足够的空间,导致FGC。此时所能做的就是尽可能合理优化参数,保证不触发这些场景。
(4)应该如何操作来加快FGC的速度
FGC的速度,在JVM层面其实已经做了很多优化,包括并行优化等。那么在此基础之上,我们还能通过什么策略来提升FGC的速度?比如要减少FGC处理的总量,靠减少堆内存来减少FGC要处理的总量吗?针对FGC的速度要做的优化,不能简单的从堆内存空间大小来考虑。
G1提供了一种思路,叫"弥留空间"。当G1发现经历多次GC后,就会允许一定比例的空间,作为把垃圾对象当成存活对象处理的空间。这些垃圾对象所在的一定范围的区域,可以成为弥留空间。虽然这个弥留空间是垃圾对象,但在GC处理时,是当作存活对象来处理。
FGC在处理这块弥留空间里的对象时,会把它们直接当作存活对象来处理,不需要做各种复杂的标记、判定引用、指针替换等各种操作,直接去复制。明知是死亡对象,但此时先不做全部空间的标记压缩整理,而只做部分的。所以弥留空间就能快速被跳过,减少处理空间,一定程度提高FGC的速率。弥留空间,就是为了提升FGC的效率而设计的。
2.一个bug导致的FGC(Kafka发送重试 + subList导致List越来越大)
(1)运营场景业务分析
(2)业务场景背景介绍
(3)问题现场及问题排查
(4)问题解决
(1)运营场景业务分析
一般在电商公司里会有一个运营平台,有些公司叫营销平台,这个平台的作用主要就是拉新、增涨营收。就是通过运营平台发起活动、或者投放广告,来实现用户增长。对于平台的存量用户,也会有优惠活动、节日福利活动等来留住用户。
各种各演的运营活动,都会通过运营平台去产生。然后专门有一个抽离的运营消息推送服务来推送运营活动消息给用户。由于这些消息的数量非常庞大,对于一家几十万上百万的用户,一天假如有3次运营活动,就要至少好几百万甚至上千万的的消息要推送。这么大量的消息,不会直接全量推送,而是生成推送消息,发送到MQ,然后通过一个消费者去消费这些消息,慢慢把千万级别的消息推送给用户。
(2)业务场景背景介绍
在这个场景中,一个很重要的要点就是:运营消息推送平台会生成消息推送到消息中间件Kafka中。这个消息生成、推送至MQ的速率,很大程度影响到整个推送流程的速度。
因此,对这个消息生成和推送的过程,就需做优化,当时优化的思路是:
一.分布式生成消息,即多台机器,把用户群体分成多个部分来生成消息
二.batch推送消息,减少与消息中间件的网络通信
三.运营消息推送平台,使用多线程并发推送batch消息
四.把一些大批量的查询借助一些其他的数据搜索引擎或缓存来提升效率
比如,借助JVM本地缓存,每台机器保存一些用户账号相关信息。比如,使用ES来存储用户信息,提升多条件下的搜索查询效率。比如,使用Redis Cluster缓存,避免使用数据库导致效率低。
这些基本的思路其实还是比较简单的,细节上的优化就是:batch的大小如何确定、多线程推送时线程池的线程数量要怎么设置等。
(3)问题现场及问题排查
一.引发OOM的原因是频繁FGC
然后在一次优化测试上线后,运行一段时间,没有什么大问题。在某天Kafka服务发生了一次抖动,持续时间大概5-6s的时间,然后发现这个运营平台直接崩溃了。
在优化前,这个运营平台的推送效率有待提升,但是系统还是很稳定的。出现这个问题后紧急查看了报错日志,发现是堆内存OOM导致进程崩溃。
于是下载GC日志,下载内存快照文件,查看dump快照文件,最后发现是batch推送时线程池中的线程持有了大量的大List对象。
本来是打算找OOM的原因,解决OOM问题的。但发现在OOM前出现了大量的FGC,而大量的FGC才最终导致系统崩溃。
那么结合dump快照里大量的List对象,很容易想到是大量的List导致频繁FGC。而大量的List都还存活,所以最终导致OOM。
二.频繁FGC的原因是List造成的对象过大
那么为什么会有这么多次FGC,为什么有这么多的List没被回收?通过代码排查,发现了一个很严重的代码bug,这个bug其实很好规避。
原来在代码中采取的策略是:每次会查询一组用户数据,这组用户数据会有2000个用户。然后将这组数据封装成消息体是按照一个List放200个用户去封装,也就是将200个用户封装成一个List消息体之后,才会发送到Kafka中。
注意:Kafka本身也有一个缓冲机制来实现batch发送。我们的代码将消息一条一条发送时,Kafka会在本地客户端暂存。存到一定大小时,才会按照一个批次推送到Kafka服务端。
这种策略正常来说是没问题的,因为200个用户封装后的大小也不大。但是写代码时,因为运营的一个需求,把其中一段代码修改了一下。运营要求在某个促销活动下,消息必须推送到用户侧,不能出现漏发错发多发。
原有的代码是:把拿到的2000一组的用户数据,拆分成10个batch,然后推送至Kafka。假如推送失败就不管了,因为漏发某个用户的消息也问题不大。
修改后的代码是:针对这种特殊活动,添加了一些逻辑,即等待Kafka返回推送结果。如果拿到推送结果则表明推送成功,则执行完毕。如果拿不到推送结果则表明推送失败,需要把失败的用户集,重新加入到一个集合中去重试推送。具体的伪代码如下:
//获取一组用户信息
ArrayList list = getUserList();
List subList;
List failList = new ArrayList();
int index = 0;
//拆分用户信息
while(true) {
if (index >= list.size()-1) {
break;
}
subList = list.subList(index, index + 200);
kafkaPushResult = sendToKafka();
//等待发送结果
if (kafkaPushResult == false) {
//发送失败,是第一次,就把发送失败的subList赋值给failList
if (index == 0) {
failList = subList;
}
//不是第一次,就把发送失败的全部加入到failList中,等待后续继续发送
failList.addAll(subList);
}
}
上述代码看起来是不是没什么问题?事实上,当这个kafkaPushResult失败没有触发时,确实不会出什么问题。但是一旦触发了失败,就会出现问题了。
原因在于对于List来说,subList不是新建了一个对象,而是把大的list的其中一段使用了两个指针去指向它,所以本质上subList还是大的list的一部分。
那么如果频繁触发发送失败,failList这么写就相当于是大的list的一部分。那么执行failList.addAll(subList)代码时,相当于是在把这部分失败的元素,加入到大的list里。
所以,list是会越来越大。如果一次推送几十万用户的消息,一个线程池里面设置30-60个线程。那么在Kafka抖动的几秒的这段时间,这些list会极速扩张好几千倍不止。也就是说,假设这5s内,一共能拿到10w的用户数据。那么在5s内就会膨胀到几千万甚至上亿的数据,紧接着,重试操作会针对这些数据重试,而这个大的list又暂时不释放。如果Kafka抖动时间短,经历几次FGC就完成重新推送,没什么问题了。如果Kafka经常抖动或抖动时间长,就会造成频繁的FGC,甚至OOM。
这个场景,由于FGC产生的原因比较直接。所以分析时很容易通过GC日志 + dump快照文件迅速定位了问题。但很多FGC场景,产生的原因各不相同,分析过程还是会非常复杂的。不过基本思路都是:系统日志 -> 监控数据 -> GC日志 -> dump文件 -> 代码反查 -> bug复现 -> 解决问题。
(4)问题解决
知道了问题原因,其实就很好解决了。根本原因还是因为在写代码时对api源码的实现不熟悉,同时也没有深入检查代码的习惯,导致在做一些业务处理时出现了意想不到的bug。
对于这个代码,其实只需要做一个改动就OK了。在外层初始化failList,并且在需要把subList数据作为数据源的操作时,使用addAll操作,把它加入到新的list中再进行操作即可。
failList = new ArrayList();
if (index == 0) {failList = subList;
}->
failList.addAll(subList)
在使用subList拆分list时,一定要熟悉一下这个subList的源码实现方式。往往JDK的一些优化手段,会给我们程序造成一些不必要的问题。
此外,如果出现了频繁FGC,很有可能是对象产生的速度和垃圾回收的速度匹配不上,回收的量不够就会有可能OOM了。
3.为什么G1的FGC比ParNew + CMS要更严重
(1)ParNew + CMS的FGC触发
(2)G1的FGC触发
(3)G1的FGC更加恐怖的原因总结
(1)ParNew + CMS的FGC触发
ParNew + CMS触发的FGC的规则其实还是比较简单的,总结来说,就是老年代不够用了。
当然老年代不够用的过程可能比较多:新生代晋升、大对象占用等。但使用ParNew + CMS时,新生代和老年代的比例往往都是比较均衡,有些系统新生代甚至远大于老年代。
那么在这种场景下,FGC相对来说回收的空间就不算太恐怖,毕竟回收的空间只有堆内存的1/2左右。比较耗时的地方就是:全量标记、对象复制、压缩整理的过程。因此FGC即使偶尔发生一次,比如一天一次或几小时一次,也能接受。
并且老年代对象的晋升,是有一系列的担保机制的,比如:老年代剩余内存大于新生代存活对象、老年代剩余内存大小大于历次新生代晋升到老年代的对象的平均大小等。
因此综合分析下来,ParNew + CMS触发FGC时,从空间上和处理算法(标记整理)上来说,偶尔一次还是能够接受的。
(2)G1的FGC触发
G1的FGC触发场景,基本上有两种:
一.新生代晋升失败导致的FGC
有可能是因为晋升预留空间不够导致的,比如预留的晋升空间比例参数G1ReservePercent调整为了5,而新生代区本次晋升对象比较多,此时就会发生晋升失败导致FGC。
这种情况触发的FGC稍微好一点,因为老年代的实际使用量是:老年代大小 * (1 - --XX:G1ReservePercent%)。并且因为是晋升导致的FGC,此时新生代是刚执行完新生代回收的。所以基本上这种情况下导致的FGC,只需回收相对比较小的区域即可。
即便如此,这种情况下的FGC效率依然很低,因为涉及到大量的标记、压缩、整理、引用处理等各种操作。
二.对象分配失败导致的FGC
对象分配失败导致的FGC,就比较恐怖了。因为在对象分配失败时,会经历如下整个过程:TLAB -> TLAB扩展 -> 堆内存分配 -> Region扩展 -> YGC -> MGC -> 堆扩展 -> 分配失败。
如果是这个过程造成分配失败,则意味着整个堆内存的使用率非常高。即使经过了YGC + MGC + 扩展操作,还是无法成功分配。
这时进入的FGC就可以理解为:几乎大部分的内存都被占用了,连一个对象都无法成功分配。此时的FGC需要处理的Region,几乎是整个堆内存里的所有Region。
并且结合FGC的整个处理过程:标记->对象头处理->对象移动->压缩->清理->Rset处理等一系列操作,此时的FGC就会非常耗时。
所以这个情况下触发的FGC会非常恐怖,当然如果没有乱调参数,正常情况也不会发生这么恐怖的FGC。
三.总结
如果YGC执行后晋升失败导致FGC,那么就是reserve空间不够导致的。也就是分配对象失败,执行YGC后,必然会发生晋升,此时有可能就会在晋升失败前触发FGC来清理了。所以基本上不会出现达到多次YGC + MGC之后,还无法分配成功的情况。
但要注意有可能会出现:QPS暴增,对象产生速度比较快,然后回收速度比较慢,虽然经历了多次YGC,但垃圾对象依然很多的情况。
(3)G1的FGC更加恐怖的原因总结
一.G1通常来说要管理更大的堆内存空间,因此需要处理更多的对象
二.G1触发FGC的条件比较苛刻,分配失败的FGC需处理整个堆内存
三.G1在执行FGC过程中,需要针对复杂的Rset引用关系做更多处理
4.FGC的一些参数及优化思路(都围绕回收速度跟不上垃圾产生速度展开)
(1)-XX:G1HeapRegionSize
(2)-XX:G1ReservePercent默认是10
(3)-XX:MaxGCPauseMillis停顿时间
(4)-XX:InitiatingHeapOccupancyPercent
(5)-XX:ConcGCThreads
(6)-XX:G1ConcMarkStepDurationMillis
(7)-XX:MarkSweepAlwaysCompactCount
(1)-XX:G1HeapRegionSize
这个参数用于控制Region大小,调整这个参数可以避免老年代中的大对象占用过多的内存。因为老年代大对象占用过多的内存,就会提高老年代的使用率。老年代的使用率高了,就必然会增加晋升失败的概率。所以增大Region大小,可以避免不算太大的对象进入老年代,从而降低晋升失败的概率。
调大RegionSize的另外一个优点是:在发生对象复制、晋升时,PLAB也会相对大一些,从而在复制、晋升时,速率也会提升。
(2)-XX:G1ReservePercent默认是10
这个参数代表老年代预留给新生代对象晋升的空间占用堆内存的比例,如果经常因为晋升失败导致FGC,说明这个值太小。此时可适当调高这个值,降低FGC频率。
(3)-XX:MaxGCPauseMillis停顿时间
这个参数设置是否合理,关系到了垃圾回收的效率。假如设置了20ms的停顿时间,则很有可能导致每次GC回收的垃圾非常少。假如系统并发非常高,产生垃圾的速度非常快,就有可能会不断进行YGC。但是每次YGC都只能回收掉很少一部分垃圾,最终造成FGC。
所以,一个合理的停顿时间设置,是非常有必要的。一般情况下,系统的停顿时间可以设置100-200ms之间,具体情况需要根据系统运行情况及JVM监控情况来调整。
(4)-XX:InitiatingHeapOccupancyPercent
意思是当堆内存的占用比例达45%时,就会触发并发标记(可能开启MGC)。假如因为垃圾回收的速率跟不上系统产生垃圾的速度而造成频繁的FGC,那么就可以适当调低这个参数,尽快开启MGC,通过提升MGC的频率来避免JVM内堆积过多的垃圾对象。
注意:这个参数调小造成的多次GC和停顿时间造成的多次GC不是一个概念。
停顿时间造成的GC:是因为每次停顿时间不够,只能回收很少的垃圾,导致垃圾堆积最终FGC。
调小InitiatingHeapOccupancyPercent这个参数:此时停顿时间固定,但是回收的频率提升上来了。这样在同样的程序运行时间里,能够回收更多的垃圾,可以避免FGC。
(5)-XX:ConcGCThreads
这个参数是指在标记过程中的并行标记(STW)线程数量。如果因为并发标记不够及时,并发标记时间过长,导致垃圾回收的速率跟不上,造成FGC。
略微提升这个值,可能能够提升标记速度,以达到避免FGC的效果。当然线程数量多少,要和服务器的配置相匹配,不能盲目调大这个值。
(6)-XX:G1ConcMarkStepDurationMillis
这个参数指的是并发标记(不会STW)执行的时间,默认是10ms。调小该参数可以提升并发标记的次数,让并发标记触发的更加频繁一点,从而让重新标记更短一点,以此来提升垃圾回收的速率,避免垃圾回收速度跟不上垃圾产生速度,最终造成FGC。
正常来说这个参数也是不需要调整的,只有发现系统经常因为垃圾处理不及时而频繁的触发FGC,才有可能需要调小这个参数。
(7)-XX:MarkSweepAlwaysCompactCount
这个参数表示,经过多次GC后:允许JVM内存中有一定比例的空间用来将垃圾对象当作存活对象来处理,可以称这块儿空间为弥留空间。
弥留空间主要的目的就是可以把垃圾对象当作存活对象来处理,相当于给了一块比较好处理的空间,能够减少FGC时的处理压力。可以说是间接"减少FGC需要处理的空间",这个空间不宜太大。如果太大就会造成一部分空间被占用,一般来说保持默认即可。
(8)总结
上面介绍的这些参数,多数都是以"避免"FGC的思路来展开优化。只有第七个参数是通过为FGC"减负"的思路来展开优化,而且避免FGC通常都是从提升垃圾回收的速率这个角度出发:让垃圾回收速率赶上垃圾对象产生的速率。
对于FGC来说,"减负"这个思路,在多数情况下是无法采取的。因为FGC的特性就决定了不太可能通过其本身的参数调整来提速,更多的优化思路和优化手段还是要从"避免"角度出发,避免大多数FGC才是优化的重点。
回收速度跟不上垃圾产生速度总结:
一.调大RegionSize,让PLAB增大,从而加快复制、晋升的速度
二.调大老年代预留给新生代晋升的占比,降低晋升失败的频率
三.调小触发MGC的老年代占比,加快MGC
四.增加并发标记的线程,加快GC
五.降低每次并发标记的执行时间,降低重新标记时间,加快GC
六.调大弥留空间占比,降低FGC的处理压力,提升FGC的处理速度
为什么G1的FGC比ParNew + CMS要更严重:
一.ParNew + CMS的FGC触发
就是老年代不够用了。
二.G1的FGC触发条件
条件一:新生代晋升失败导致的FGC。
条件二:对象分配失败导致的FGC。
三.G1的FGC更加恐怖的原因总结
原因一:G1通常来说要管理更大的堆内存空间,因此需要处理更多的对象。
原因二:G1触发FGC的条件比较苛刻,分配失败的FGC需处理整个堆内存。
原因三:G1在执行FGC过程中,需要针对复杂的Rset引用关系做更多处理。
相关文章:

G1原理—10.如何优化G1中的FGC
大纲 1.G1的FGC可以优化的点 2.一个bug导致的FGC(Kafka发送重试 subList导致List越来越大) 3.为什么G1的FGC比ParNew CMS要更严重 4.FGC的一些参数及优化思路 1.G1的FGC可以优化的点 (1)FGC的基本原理 (2)遇到FGC应该怎么处理 (3)应该如何操作来规避FGC (4)应该如何操…...

Java基础——概念和常识(语言特点、JVM、JDK、JRE、AOT/JIT等介绍)
我是一个计算机专业研0的学生卡蒙Camel🐫🐫🐫(刚保研) 记录每天学习过程(主要学习Java、python、人工智能),总结知识点(内容来自:自我总结网上借鉴࿰…...

2025.1.16——三、supersqli 绕过|堆叠注入|handler查询法|预编译绕过法|修改原查询法
题目来源:攻防世界supersqli 目录 一、打开靶机,整理已知信息 二、sqlmap解题 step 1:爆数据库 step 2:爆表 二、手工注入解题 step 1:判断注入类型 step 2:判断字段数 step 3:查询数据…...

浅谈计算机网络03 | 现代网络组成
现代网络组成 一 、网络生态体系1.1网络生态系统的多元主体1.2 网络接入设施的多样类型 二、现代网络的典型体系结构解析三、高速网络技术3.1 以太网技术3.2 Wi-Fi技术的深度剖析3.2.1 应用场景的多元覆盖3.2.2 标准升级与性能提升 3.3 4G/5G蜂窝网的技术演进3.3.1 蜂窝技术的代…...

Red Hat8:搭建FTP服务器
目录 一、匿名FTP访问 1、新建挂载文件 2、挂载 3、关闭防火墙 4、搭建yum源 5、安装VSFTPD 6、 打开配置文件 7、设置配置文件如下几个参数 8、重启vsftpd服务 9、进入图形化界面配置网络 10、查看IP地址 11、安装ftp服务 12、遇到拒绝连接 13、测试 二、本地…...

EWM 批次管理 / Batch Management
目录 1 简介 2 业务数据 2.1 基于 PO,创建 ERP LE - Delivery 内向交货单,同时同步到 EWM 内向交货单 2.2 在 EWM 内向交货单,创建批次。EWM 批次创建的前提条件来自于物料主数据批次分类(023)。SAP 提供的标准条件…...
Java 面试题 - ArrayList 和 LinkedList 的区别,哪个集合是线程安全的?
Java 面试题 - ArrayList 和 LinkedList 的区别,哪个集合是线程安全的? 在 Java 开发中,ArrayList和LinkedList是两个常用的集合类,它们在数据结构和性能上有诸多不同,同时线程安全性也各有特点。深入理解这些差异&am…...

初学SpringBoot
目录 什么是SpringBoot 使用 Spring Boot有什么好处 Spring Boot 特点 在线构建 IntelliJ IDEA在线模板构建 IntelliJ IDEA 通maven项目构建 SpringBoot的常用配置 入口类和相关注解 定制Banner 修改banner图标 关闭banner 常规属性修改 tomcat端口号修改 常规属性…...
【网络云SRE运维开发】2025第3周-每日【2025/01/15】小测-【第14章ospf高级配置】理论和实操解析
文章目录 14.1 选择题解题思路和参考答案14.2 理论题解题思路和参考答案14.3 实操题解题思路和参考答案思科(Cisco)设备华为(Huawei)设备小米/锐捷(或其他支持标准CLI命令的设备)通过网络管理工具注意事项 …...
AWS S3 跨账户访问 Cross Account Access
进入S3对应的存储桶,上面选项选权限,存储桶策略 -- 编辑,输入对应的policy。 完全控制,包含上传删除权限,policy如下: {"Version": "2012-10-17","Statement": [{"Si…...
Ubuntu20.4和docker终端指令、安装Go环境、安装搜狗输入法、安装WPS2019:保姆级图文详解
目录 前言1、docker、node、curl版本查看终端命令1.1、查看docker版本1.2、查看node.js版本1.3、查看curl版本1.4、Ubuntu安装curl1.5、Ubuntu终端保存命令 2、安装docker-compose、Go语言2.1、安装docker-compose2.2、go语言安装步骤2.3、git版本查看 3、Ubuntu20.4安装搜狗输…...
Kotlin语言的正则表达式
Kotlin语言中的正则表达式 引言 正则表达式(Regular Expression,简称Regex)是一种用于匹配字符串中字符组合的工具。在数据处理、文本解析等领域,正则表达式以其强大的字符串处理能力得到了广泛的应用。而Kotlin作为一种现代的编…...

npm的包管理
从哪里下载包 国外有一家 IT 公司,叫做 npm,Inc.这家公司旗下有一个非常著名的网站: https://www.npmjs.com/,它是全球最大的包共享平台,你可以从这个网站上搜索到任何你需要的包,只要你有足够的耐心!到目前位置,全球约…...
深度学习在文本情感分析中的应用
引言 情感分析是自然语言处理(NLP)中的一个重要任务,旨在识别和提取文本中的主观信息。随着深度学习技术的发展,我们可以使用深度学习模型来提高情感分析的准确性和效率。本文将介绍如何使用深度学习进行文本情感分析,…...

【大模型系列篇】数字人音唇同步模型——腾讯开源MuseTalk
之前有一期我们体验了阿里开源的半身数字人项目EchoMimicV2,感兴趣的小伙伴可跳转至《AI半身数字人开箱体验——开源项目EchoMimicV2》,今天带大家来体验腾讯开源的数字人音唇同步模型MuseTalk。 MuseTalk 是一个实时高品质音频驱动的唇形同步模型&#…...

Formality:参考设计/实现设计以及顶层设计
相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 Formality存在两个重要的概念:参考设计/实现设计和顶层设计,本文就将对此进行详细阐述。参考设计/实现设计是中两个重要的全局概念&am…...

RPA赋能内容创作:打造小红书入门词语图片的全自动化流程
🌟 嗨,我是LucianaiB! 🌍 总有人间一两风,填我十万八千梦。 🚀 路漫漫其修远兮,吾将上下而求索。 用RPA全自动化批量生产【入门词语】图片做小红书商单,保姆级工具开发教程 最近由…...
RPC 源码解析~Apache Dubbo
解析 RPC(远程过程调用)的源码可以帮助你深入理解其工作原理和实现细节。为了更好地进行源码解析,我们选择一个流行的 RPC 框架——Apache Dubbo 作为示例。Dubbo 是一个高性能、轻量级的开源 Java RPC 框架,广泛应用于企业级应用…...
VS Code--常用的插件
原文网址:VS Code--常用的插件_IT利刃出鞘的博客-CSDN博客 简介 本文介绍VS Code(Visual Studio Code)常用的插件。 插件的配置 默认情况下,插件会放到这里:C:\Users\xxx\.vscode\extensions 修改插件位置的方法 …...
深度学习 Pytorch 张量的索引、分片、合并以及维度调整
张量作为有序的序列,也是具备数值索引的功能,并且基本索引方法和python原生的列表、numpy中的数组基本一致。 不同的是,pytorch中还定义了一种采用函数来进行索引的方式。 作为pytorch中的基本数据类型,张量既具备了列表、数组的基…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...

软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...
Django RBAC项目后端实战 - 03 DRF权限控制实现
项目背景 在上一篇文章中,我们完成了JWT认证系统的集成。本篇文章将实现基于Redis的RBAC权限控制系统,为系统提供细粒度的权限控制。 开发目标 实现基于Redis的权限缓存机制开发DRF权限控制类实现权限管理API配置权限白名单 前置配置 在开始开发权限…...

spring boot使用HttpServletResponse实现sse后端流式输出消息
1.以前只是看过SSE的相关文章,没有具体实践,这次接入AI大模型使用到了流式输出,涉及到给前端流式返回,所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...