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

JVM实战—8.如何分析jstat统计来定位GC

大纲

1.使用jstat了解线上系统的JVM运行状况

2.使用jmap和jhat了解线上系统的对象分布

3.如何分析JVM运行状况并合理优化

4.使用jstat分析模拟的BI系统JVM运行情况

5.使用jstat分析模拟的计算系统JVM运行情况

6.问题汇总

1.使用jstat了解线上系统的JVM运行状况

(1)JVM的整体运行原理简单总结

(2)功能强大的jstat

(3)jstat -gc PID

(4)其他的jstat命令

(5)到底该如何使用jstat工具

(6)新生代对象增长的速率

(7)Young GC的触发频率和每次耗时

(8)每次Young GC后有多少对象进入老年代

(9)Full GC的触发时机和耗时

(1)JVM的整体运行原理简单总结

一.对象优先在Eden区分配

二.Young GC的触发时机和执行过程

三.对象进入老年代的时机

四.Full GC的触发时机和执行过程

接下来介绍如何使用工具分析运行的系统:

一.对象增长的速率

二.Young GC的触发频率

三.Young GC的耗时

四.每次Young GC后有多少对象存活下来

五.每次Young GC后有多少对象进入老年代

六.老年代对象增长的速率

七.Full GC的触发频率

八.Full GC的耗时

(2)功能强大的jstat

如果平时要对运行中的系统,检查其JVM的整体运行情况。比较实用的工具之一就是jstat,它可以轻易让我们看到当前JVM内:Eden区、S区、老年代的内存情况,以及YGC和FGC的执行次数和耗时。

通过这些指标,我们就可以轻松分析出当前系统的运行情况。从而判断当前系统的内存使用压力和GC压力,以及内存分配是否合理。

(3)jstat -gc PID

首先使用jps命令在生产机器Linux上,找出Java进程的PID。接着就针对我们的Java进程执行:jstat -gc PID,这样就可以看到这个Java进程的内存和GC情况了。运行这个命令后会看到如下指标的信息:

$ jstat -gc 1170
S0C    S1C    S0U    S1U    EC    EU    OC    OU    MC    MU    CCSC    CCSU    YGC    YGCT    FGC    FGCT    GCT    S0C:这是From Survivor区的大小,C代表的是Capacity
S1C:这是To Survivor区的大小,C代表的是Capacity
S0U:这是From Survivor区当前使用的内存大小,U代表的是Used
S1U:这是To Survivor区当前使用的内存大小,U代表的是Used
EC:这是Eden区的大小,E代表的是Eden,C代表的是Capacity
EU:这是Eden区当前使用的内存大小,E代表的是Eden,U代表的是Used
OC:这是老年代的大小,O代表的是Old,C代表的是Capacity
OU:这是老年代当前使用的内存大小,O代表的是Old,U代表的是Used
MC:这是方法区(永久代、元数据区)的大小,M代表的是Metaspace,C代表的是Capacity
MU:这是方法区(永久代、元数据区)的当前使用的内存大小,M代表的是Metaspace,U代表的是Used
YGC:这是系统运行迄今为止的Young GC次数
YGCT:这是Young GC的耗时,T代表的是Time
FGC:这是系统运行迄今为止的Full GC次数
FGCT:这是Full GC的耗时,T代表的是Time
GCT:这是所有GC的总耗时,T代表的是Time

这些指标都是非常实用的JVM GC分析指标。

(4)其他的jstat命令

除了上面的jstat -gc命令是最常用的以外,jstat还有一些命令可以看到更多详细的信息,如下所示:

jstat -gccapacity PID:堆内存分析
jstat -gcnew PID:年轻代GC分析,这里的TT和MTT可以看到对象在年轻代存活的年龄和存活的最大年龄
jstat -gcnewcapacity PID:年轻代内存分析
jstat -gcold PID:老年代GC分析
jstat -gcoldcapacity PID:老年代内存分析
jstat -gcmetacapacity PID:元数据区内存分析

但最完整、最常用、最实用的还是jstat -gc命令,jstat -gc命令基本足够分析JVM的运行情况了。

(5)到底该如何使用jstat工具

首先需要明确,分析线上的JVM进程,最想要知道的信息有哪些?最想要知道的信息会包括如下:

一.新生代对象增长的速率

二.Young GC的触发频率

三.Young GC的耗时

四.每次Young GC后有多少对象存活下来

五.每次Young GC后有多少对象进入老年代

六.老年代对象增长的速率

七.Full GC的触发频率

八.Full GC的耗时

只要知道了这些信息,我们就可以结合前面介绍的JVM GC优化的方法:合理分配内存,尽量让对象留在年轻代不进入老年代,避免频繁FGC。这就是对JVM最好的性能优化了。

(6)新生代对象增长的速率

需要了解JVM的第一个信息就是:随着系统运行,每秒会在新生代的Eden区分配多少对象。

要获取该信息,只需要在Linux机器上运行命令:jstat -gc PID 1000 10,该命令意思:每隔1秒更新最新jstat统计信息,一共执行10次jstat统计。通过这行命令,可以灵活地对线上机器,通过固定频率输出统计信息。从而观察出每隔一段时间,JVM中Eden区的对象占用变化。

比如执行这行命令后:第一秒显示出Eden区使用了200M内存,第二秒显示出Eden区使用了205M内存,第三秒显示出Eden区使用了209M内存,以此类推。此时我们就可以推断出来,这个系统大概每秒钟会新增5M左右的对象。

而且这里可以根据自己系统的情况灵活多变地使用,如果系统负载很低,则不一定每秒进行统计,可以每分或每10分来统计,以此查看系统每隔1分钟或者10分钟大概增长多少对象。

此外,系统一般有高峰和日常两种状态,比如系统高峰期用户很多,我们应该在系统高峰期去用上述命令看看高峰期的对象增长速率,然后再在非高峰的日常时间段内看看对象的增长速率,这样就可以了解清楚系统的高峰和日常时间段内的对象增长速率了。

(7)Young GC的触发频率和每次耗时

接着需要了解JVM的第二个信息是:大概多久会触发一次YGC,以及每次YGC的耗时。其实多久触发一次YGC是很容易推测出来的,因为系统高峰和日常时的对象增长速率都知道了,根据对象增长速率和Eden区大小,就可以推测出:高峰期多久发生一次YGC,日常期多久发生一次YGC。

比如Eden区有800M内存:如果发现高峰期每秒新增5M对象,那么大概3分钟会触发一次YGC。如果发现日常期每秒新增0.5M对象,那么大概半小时才触发一次YGC。

那么每次Young GC的平均耗时呢?jstat会展示迄今为止系统已经发生了多少次YGC以及这些YGC的总耗时。比如系统运行24小时后共发生260次YGC,总耗时为20s。那么平均每次YGC大概耗时几十毫秒,我们由此可以大概知道每次YGC时会导致系统停顿几十毫秒。

(8)每次Young GC后有多少对象进入老年代

接着要了解JVM的第三个信息是:每次YGC后有多少存活对象,即有多少对象会进入老年代。

其实每次YGC过后有多少对象会存活下来,只能大致推测出来。假设已经推算出高峰期多久会发生一次YGC,比如3分钟会有一次YGC。那么此时就可以执行下述jstat命令:jstat -gc PID 180000 10。这就相当于让JVM每隔三分钟执行一次统计,连续执行10次。观察每隔三分钟发生一次YGC时,Eden、Survivor、老年代的对象变化。

正常来说:Eden区肯定会在几乎放满后又变得很少对象,比如800M只使用几十M。Survivor区肯定会放入一些存活对象,老年代可能会增长一些对象占用。所以这时观察的关键,就是观察老年代的对象增长速率。

正常来说:老年代不太可能不停快速增长的,因为普通系统没那么多长期存活对象。如果每次YGC后,老年代对象都要增长几十M,则可能存活对象太多了。存活对象太多可能会导致放入S区后触发动态年龄判定规则进入老年代,存活对象太多也可能导致S区放不下,大部分存活对象需要进入老年代。

如果老年代每次在YGC过后就新增几百K或几M的对象,这个还算正常。但如果老年代对象快速过快增长,那一定是不正常的。

所以通过上述观察策略,就可以知道每次YGC后有多少对象是存活的,也就是Survivor区里增长的 + 老年代增长的对象,就是存活的对象。

通过jstat -gc也可以知道老年代对象的增长速率,比如每隔3分钟一次YGC,每次会有50M对象进入老年代,于是老年代对象的增长速率就是每隔3分钟增长50M。

(9)Full GC的触发时机和耗时

只要知道老年代对象的增长速率,那么Full GC的触发时机就很清晰了。比如老年代有800M,每3分钟新增50M,则每1小时就会触发一次FGC。

根据jstat输出的系统运行迄今为止的FGC次数以及总耗时,就能计算出每次FGC耗时。比如一共执行了10次FGC,共耗时30s,那么每次FGC大概耗费3s左右。

2.使用jmap和jhat了解线上系统的对象分布

(1)jstat总结

(2)使用jmap了解系统运行时的内存区域

(3)使用jmap了解系统运行时的对象分布

(4)使用jmap生成堆内存转储快照

(5)使用jhat在浏览器中分析堆转出快照

(1)jstat总结

通过jstat可以非常轻松便捷的了解到线上系统的运行状况,如新生代对象和老年代对象增速、YGC和FGC触发频率以及耗时等,通过jstat可以完全了解线上系统的JVM运行情况,为优化做准备。

接下来介绍两个在工作中非常实用的工具:jmap和jhat。这两个工具可以观察线上JVM中的对象分布,能更加细致了解系统运行。即了解系统运行过程中,哪些对象占据了大部分,占据了多少内存空间。

(2)使用jmap了解系统运行时的内存区域

其实如果只是了解JVM运行状况,然后进行GC优化,通常jstat就够用了。但有时会发现JVM新增对象速度很快,想知道什么对象占那么多内存。从而可以优化对象在代码中的创建时机,避免对象占用内存过大。

首先看一个命令:jmap -heap PID。这个命令可以打印出一系列信息,这些信息大概就是:堆内存相关的一些参数设置、当前堆内存里各个区域的情况。比如:Eden区的总容量、已经使用的容量、剩余的空间容量,两个Survivor区的总容量、已经使用的容量、剩余的空间容量,老年代的总容量、已经使用的容量、剩余的容量。

但是这些信息其实jstat已经有了,所以一般不会用jmap去获取这些信息。毕竟jmap的这种信息还没jstat全面,比如jmap就没有GC相关的统计。

(3)使用jmap了解系统运行时的对象分布

jmap中比较有用的一个命令是:jmap -histo PID。这个命令会打印出类似下面的信息:按各对象占用内存空间大小降序排列,把占用内存最多的对象放在最上面。

num       #instances          #bytes      class name
----------------------------------------------------
1:        46608               1111232     java.lang.String
2:         6919                734516     java.lang.Class
3:         4787                536164     java.net.SocksSocketImpl
4:        15935                497100     java.util.concurrent.ConcurrentHashMap$Node
5:        28561                436016     java.lang.Object

所以想简单了解当前JVM中的对象内存占用情况,可用jmap -histo命令。该命令可快速了解当前内存里到底哪个对象占用了大量的内存空间。

(4)使用jmap生成堆内存转储快照

如果想查看对象占用内存的具体情况,那么可以使用jmap命令生成一个堆内存转储快照到文件里:

jmap -dump:live,format=b,file=dump.hprof PID

这个命令会在当前目录下生成一个dump.hrpof文件,该文件是二进制的格式,不能直接打开看,这个命令会把这一时刻JVM堆内存里所有对象的快照放到文件里。

(5)使用jhat在浏览器中分析堆转出快照

可以使用jhat去分析堆内存快照,jhat内置了web服务器,支持通过web界面来分析堆内存转储快照。

使用如下命令即可启动jhat服务器,可以指定HTTP的端口,默认的HTTP端口为7000。

jhat  -port 7000 dump.hprof

接着就可以在浏览器上访问当前这台机器的7000端口号,这样就可以通过图形化的方式去分析堆内存里的对象分布情况了。

3.如何分析JVM运行状况并合理优化

(1)开发好系统后的预估性优化

(2)系统压测时的JVM优化

(3)对线上系统进行JVM监控

(1)开发好系统后的预估性优化

什么叫预估性优化?就是估算系统每秒多少请求、每个请求创建多少对象、占用多少内存。机器应选用什么配置、新生代应给多少内存、老年代应给多少内存。Young GC触发的频率、对象进入老年代的速率、Full GC触发的频率。

这些信息其实都是可以根据系统代码,大致合理地进行预估的。在预估完成后,就可以采用前面的优化思路,先设置初始的JVM参数。比如堆内存大小、新生代大小、Eden和Survivor的比例、老年代大小、大对象的阈值、大龄对象进入老年代的阈值等。

优化思路就是:尽量让每次YGC后的存活对象小于S区的50%,可以都留在新生代里。尽量别让对象进入老年代,减少FGC的频率、避免频繁FGC影响性能。

(2)系统压测时的JVM优化

通常一个新系统开发完毕后,会经过一连串的测试,本地单元测试->系统集成测试->测试环境功能测试->预发环境压力测试。总之要保证系统的功能正常,在一定压力下稳定性和并发能力也都正常,最后才会部署到生产环境运行。

这里非常关键的一个环节就是预发布环境的压力测试,通常该环节会使用一些压力测试工具模拟如1000个用户同时访问系统。模拟每秒500个请求压力,然后看系统能否支撑住每秒500请求的压力。同时看系统各接口响应延时是否在比如200ms内,即接口性能不能太慢。或者在数据库中模拟出百万级单表数据,然后看系统是否还能稳定运行。

具体如何进行系统压测,可以搜Java压力测试,会有很多开源工具。通过这些工具可以轻松模拟出N个用户同时访问你系统的场景,同时还能生成压力测试报告:每秒可支撑多少请求、接口的响应延时等。

在该环节,压测工具会对系统发起持续不断的请求。这些请求通常会持续很长时间,如几小时甚至几天。所以可以在该环节,对测试机器运行的系统,采用jstat工具来进行分析。在模拟真实环境的压力测试下,通过jstat命令获取JVM的整体运行状态。

前面已具体介绍了如何使用jstat来分析以下JVM的关键运行指标:新生代对象增长的速率、YGC的触发频率、YGC的耗时、每次YGC后有多少对象存活下来、每次YGC后有多少对象进入老年代、老年代对象增长的速率、FGC的触发频率、FGC的耗时。

然后根据压测环境中的JVM运行状况:如果发现对象过快进入老年代,可能是因为年轻代太小导致频繁YGC;也可能因为很多对象存活,而S区太小,导致很多对象频繁进入老年代;此时就需要采用前面介绍的优化思路:合理调整新生代、老年代、Eden、Survivor各个区域的内存大小,保证对象尽量留在年轻代、不要过快进入老年代中。

有很多人会胡乱搜索网上JVM优化博客,看人家怎么优化就怎么优化。比如很多博客说新生代和老年代的占比一般是3:8,这其实是很片面的。每个系统都是不一样,特点不同、复杂度不同。真正的优化,一定是在实际观察我们的系统后,合理调整内存分布。真正的优化,并没有固定的JVM优化模板。当优化好压测环境的JVM参数,观察YGC和FGC频率都很低,就可上线。

(3)对线上系统进行JVM监控

当系统上线后,就需要对线上系统的JVM进行监控,这个监控通常来说有两种办法。

第一种方法:

每天在高峰期和低峰期用jstat、jmap、jhat看线上JVM运行是否正常。有没有频繁FGC问题,如果有就优化,没有就每天定时或每周定时去看。

第二种方法:

部署专门的监控系统,常见的有Zabbix、OpenFalcon、Ganglia等。可以将线上系统的JVM统计项发送到这些监控系统里去,这样就可以在这些监控系统可视化界面里,看到需要的所有指标:包括各内存区域的对象占用变化曲线、可直接看到Eden区的对象增速、YGC发生的频率以及耗时、老年代的对象增速、FGC的频率和耗时。而且这些工具通常还允许设置监控告警,比如10分钟之内发生5次以上FGC,就需要发送告警给我们。

4.使用jstat分析模拟的BI系统JVM运行情况

(1)服务于百万级商家的BI系统是什么

(2)刚开始上线BI系统时的部署架构

(3)实时自动刷新报表 + 大数据量报表

(4)没什么大影响的频繁Young GC

(5)模拟程序的JVM参数设置

(6)模拟程序

(7)通过jstat观察程序的运行状态

(1)服务于百万级商家的BI系统是什么

作为一个电商平台,可能会有数十万到百万的商家在平台上做生意。电商平台每天会产生大量数据,需要基于这些数据为商家提供数据报表。比如:每个商家每天有多少访客、有多少交易、付费转化率是多少。

BI系统其实就是把商家日常经营的数据收集起来进行分析,然后提供各种数据报表给商家的一套系统。

这样的一个BI系统,其运行逻辑如下:首先电商平台会提供一个业务平台给商家进行日常使用交互,该业务平台会采集到商家的很多日常经营数据。根据这些日常经营数据,通过Hadoop、Spark等技术计算各种数据报表,这些数据报表会被放入存储到MySQL、Elastcisearch、HBase中。最后基于MySQL、HBase、ES中存储的数据报表,开发出一个BI系统。通过这个BI系统就能把各种存储好的数据展示给商家进行筛选和分析。

图片

(2)刚开始上线BI系统时的部署架构

刚开始系统上线时,这个BI系统使用的商家是不多的,比如几千个商家,所以刚开始系统部署得非常简单,就是用几台机器来部署上述的BI系统,机器都是普通的4核8G配置。在这个配置下,会给堆内存新生代分配1.5G内存,Eden区大概1G左右,如下图示:

图片

(3)实时自动刷新报表 + 大数据量报表

刚开始在少数商家的情况下,这个系统没多大问题,运行得非常良好。但使用系统的商家开始越来越多,商家数量级达到几万时就有问题了。

首先说明一下此类BI系统的特点;就是在BI系统中有一种实时数据报表,它支持前端页面有一个JS脚本,该JS脚本每隔几秒就会自动发送请求到后台刷新一下数据。如下图示:

图片

虽然只有几万商家使用该系统,但可能同时打开实时报表的商家有几千。每个商家打开报表后,前端都会每隔几秒发送请求到后台加载最新数据。于是出现部署BI系统的每台机器每秒请求达几百个,假设每秒500请求。

然后每个请求会加载出一张报表所需要的大量数据,因为BI系统可能还要针对这些数据在内存中进行计算加工,才能返回。根据测算,每个请求大概会从MySQL中加载出100K的数据进行计算。因此每秒500个请求,就要加载50M数据到内存中进行计算。

图片

(4)没什么大影响的频繁Young GC

在上述系统运行模型下,由于每秒会加载50M的数据到Eden区中,所以只要200s就会填满Eden区,然后触发YGC对新生代进行垃圾回收。当然1G左右的Eden进行YGC速度是比较快的,可能几十ms就搞定了。所以每200s频繁执行一次YGC其实对系统性能影响并不大,而且上述BI系统场景下,基本上每次YGC后存活对象可能会有几十M。

因此可能会看到如下场景:BI系统每运行几分钟就会卡顿10ms,但对用户和系统性能几乎没影响。

图片

(5)模拟程序的JVM参数设置

接下来用一段程序模拟出上述BI系统那种频繁YGC的场景,此时JVM参数如下所示:

 -XX:NewSize=104857600 -XX:MaxNewSize=104857600  -XX:InitialHeapSize=209715200 -XX:MaxHeapSize=209715200  -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15  -XX:PretenureSizeThreshold=3145728  -XX:+UseParNewGC -XX:+UseConcMarkSweepGC  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

上述把堆内存设置为了200M,把年轻代设置为100M。然后Eden区是80M,每块Survivor区是10M,老年代也是100M。

(6)模拟程序

下面是我们的模拟程序:

public class Demo {    public static void main(String[] args) throws Exception {        Thread.sleep(30000);        while(true) {            loadData();        }    }        private static void loadData() throws Exception {        byte[] data = null;        for(int i = 0; i < 50; i++) {            data = new byte[100 * 1024];        }        data = null;        Thread.sleep(1000);    }}

上述这段模拟程序的一些说明:

一.第一行代码为Thread.sleep(30000),为什么刚开始要先休眠30s?因为程序刚启动休眠30s可方便我们找到这个程序的PID,即进程ID,然后再执行jstat命令来观察程序运行时的JVM状态。

二.接着loadData()方法内的代码会循环50次,模拟每秒50个请求。然后每次请求会分配一个100K的数组,模拟每次请求从数据存储中加载出100K的数据。接着会休眠1秒,模拟这一切都是发生在1秒内的。其实这些对象都是短生存周期的对象,方法运行结束这些对象都是垃圾。

三.然后在main()方法里有一个while(true)循环,模拟系统按照每秒50个请求,每个请求加载100K数据的方式不停运行。除非我们手动终止程序,否则永不停止。

(7)通过jstat观察程序的运行状态

一.接着我们使用预定的JVM参数启动程序

此时程序会先进入一个30秒的休眠状态,于是尽快执行jps命令,查看启动程序的进程ID。如下所示:​​​​​​​

$ jps1169 Launcher1170 Demo1171 Jps517 

二.此时会发现我们运行的Demo这个程序的JVM进程ID是1170

然后尽快执行下述jstat命令:

$ jstat -gc 1170 1000 1000

这行命令的意思是:针对1170进程统计JVM运行状态,每隔1秒打印一次统计信息,连续打印1000次。

然后执行jstat开始统计,每隔一秒都会打印一行新的统计信息,过了几十秒后可看到如下所示的统计信息:

图片

三.接着先看如下图示的一段EU信息

图片

这个EU就是Eden区被使用的容量,可发现刚开始是5M左右的使用量。接着程序开始运行,每秒都会有对象增长:从5M到10M,接着15M,20M,25M,每秒都会新增5M左右的对象。这个跟上面的代码是完全吻合的,代码也是每秒会增加5M左右的对象。

四.然后当Eden区使用量达到80M左右时,再要分配5M对象就失败了

此时就会触发一次Young GC,如下图示:

图片

上面红圈的内容:Eden区的使用量从将近80M降低为3M多,这是因为一次YGC回收掉了大部分对象。

五.所以针对这个模拟代码,可以清晰的从jstat中看出如下信息:

对象增速大致为每秒5M左右,大致每十几秒会触发一次YGC。下图可以看到,YGC的触发频率,以及每次YGC的耗时。

图片

上图清晰告诉我们:一次YGC回收70多M对象,大概花费1毫秒。所以YGC其实是很快的,即使回收800M的对象,也就10毫秒左右。所以如果是线上系统,Eden区800M的话,每秒新增对象50M。十多秒一次YGC耗时10毫秒左右,系统卡顿10毫秒几乎没什么大影响。

在这个模拟代码中:80M的Eden区,每秒新增对象5M。大概十多秒触发一次YGC,每次YGC耗时在1毫秒左右。那么YGC回收1G大小的Eden区,耗时大概会在15毫秒左右,毫秒级别。

六.那么每次YGC过后会存活多少对象

上图S1U就是Survivor中被使用的内存,S1U之前一直是0,在一次YGC过后变成了633K。所以一次YGC后存活了633K的对象而已,可轻松放入10M的Survivor。

而且注意上图的OU,就是老年代被使用的内存量,在YGC前后都是0。这说明这个系统运行良好,YGC不会导致对象进入老年代。所以这个系统就几乎不需要什么优化了,因为老年代对象增速几乎为0,FGC发生频率趋向于0,对系统无影响。

因此通过这个模拟程序的运行,我们可以使用jstat分析出以下信息的:新生代对象增长的速率、YGC的触发频率、YGC的耗时、每次YGC后有多少对象是存活的、每次YGC后有多少对象进入了老年代、老年代对象增长的速率、FGC的触发频率、FGC的耗时。

5.使用jstat分析模拟的计算系统JVM运行情况

(1)一个日处理上亿数据的计算系统

(2)这个系统多久会塞满新生代

(3)触发Young GC时会有多少对象进入老年代

(4)系统运行多久老年代就会被填满

(5)这个系统运行多久老年代会触发1次Full GC

(6)该案例应该如何进行JVM优化

(7)模拟程序用的JVM参数

(8)模拟程序

(9)基于jstat分析程序运行的状态

(10)对JVM性能进行优化

(1)一个日处理上亿数据的计算系统

当时团队里自研的一个数据计算系统,日处理数据量在上亿的规模。这个系统会不停的从MySQL数据库以及其他数据源里提取大量的数据,然后加载到自己的JVM内存里来进行计算处理,如下图示:

图片

这个数据计算系统会不停的通过SQL语句和其他方式,从各种数据存储中提取数据到内存中来进行计算,大致当时的生产负载是每分钟需要执行500次数据提取和计算的任务。

由于这是一套分布式运行的系统,所以生产环境部署了多台机器。每台机器大概每分钟负责执行100次数据提取和计算的任务(15个线程),每次会提取大概1万条数据到内存计算,平均每次计算大概耗费10秒。然后每台机器4核8G,新生代和老年代分别是1.5G和1.5G的内存空间。如下图示:

图片

(2)这个系统多久会塞满新生代

现在明确了一些核心数据,那么该系统多久会塞满新生代的内存空间。既然每台机器上部署的该系统实例,每分钟会执行100次数据计算任务。每次1万条数据需要计算10秒时间,该台机器大概开启15个线程去执行。

那么先来看看每次1万条数据大概会占用多大的内存空间:这里每条数据都是比较大的,每条数据大概包含了20个字段,可认为平均每条数据的大小在1K左右,那么每次计算任务提取的1万条数据就对应10M大小。

所以如果新生代按照8:1:1的比例来分配Eden和两块Survivor的区域,按照新生代和老年代分别是1.5G和1.5G的内存空间可知,Eden区就是1.2G,每块Survivor区在100M左右。如下图示:

图片

由于每次执行一个计算任务,就要提取1万条数据到内存,每条数据1K。所以每次执行一个计算任务,JVM会在Eden区里分配10M的对象。那么由于一分钟需要执行大概100次计算任务,所以新生代里的Eden区,基本上1分钟左右就会被迅速填满。

(3)触发YGC时会有多少对象进入老年代

假设新生代的Eden区在1分钟后都塞满对象了,在继续执行计算任务时,必然会导致需要进行YGC回收部分垃圾对象。

一.在执行YGC前会先进行检查

首先会看老年代的可用内存空间是否大于新生代全部对象。此时老年代是空的,大概有1.5G的可用内存空间,而新生代的Eden区大概有1.2G对象。

图片

于是会发现老年代的可用内存空间有1.5G,新生代的对象总共有1.2G。即使一次YGC过后,即时全部对象都存活,老年代也能放的下的,所以此时会直接执行YGC。

二.执行YGC后,Eden区里有多少对象是存活的无法被垃圾回收的

由于新生代的Eden区在1分钟就塞满对象需要YGC了,而1分钟内会执行100次任务,每个计算任务处理1万条数据需要10秒钟。

假设执行YGC时,有80个计算任务都执行结束了,但还有20个计算任务共计200M的数据还在计算中。那么此时就有200M的对象是存活的,不能被垃圾回收掉。所以总共有1G对象可以进行垃圾回收,200M对象存活无法被垃圾回收。如下图示:

图片

三.此时执行一次YGC会回收1G对象,然后出现200M的存活对象

这200M的存活对象并不能放入S区,因为一块S区就100M大小,此时老年代会通过空间担保机制,让这200M对象直接进入老年代中。于是需要占用老年代里的200M内存空间,然后对Eden区进行清空。

图片

(4)系统运行多久老年代就会被填满

按照上述计算,每分钟都是一个轮回,大概算下来是每分钟都会把新生代的Eden区填满。然后触发一次YGC,接着大概会有200M左右的数据进入老年代。

假设2分钟过去了,老年代已有400M内存被占用,只有1.1G内存可用,此时老年代的可用内存空间已经少于新生代的内存大小了。所以如果第3分钟运行完毕,又要进行YGC,会做什么检查呢?如下图示:

图片

一.首先会检查老年代可用空间是否大于新生代全部对象

此时老年代的可用空间是1.1G,新生代对象的大小有1.2G,如果这次YGC过后新生代对象全部存活,那么老年代是放不下的。

二.接着就得检查HandlePromotionFailure参数是否打开

如果"-XX:-HandlePromotionFailure"参数被打开了,一般都会打开。此时会进入下一个检查:老年代可用空间是否大于历次YGC过后进入老年代的对象的平均大小。

前面已计算出大概每分钟会执行一次YGC,每次200M对象进入老年代。此时老年代可用1.1G,大于每次YGC进入老年代的对象平均大小200M。所以可推测本次YGC后大概率还是有200M对象进入老年代,1.1G足够。因此这时就可以放心执行一次YGC,然后又有200M对象进入老年代。

三.转折点大概在运行了7分钟后

执行了7次YGC后,大概1.4G对象进入老年代。老年代剩余空间不到100M,几乎满了。如下图示:

图片

(5)这个系统运行多久老年代会触发1次Full GC

大概在第8分钟运行结束时,新生代又满了。执行YGC之前进行检查,发现老年代此时只有100M的可用内存空间。小于历次YGC后进入老年代的200M对象,于是就会直接触发一次FGC,FGC会把老年代的垃圾对象都给回收掉。

假设此时老年代被占据的1.4G空间里,全部都是可以回收的对象,那么此时就会一次性把这些对象都给回收掉。如下图示:

图片

然后执行完FGC后,还会继续执行YGC,又有200M对象进入老年代。之前的FGC就是为这次新生代YGC后要进入老年代的对象准备的。如下图示:

图片

所以根据这个运行模型,该系统平均八分钟会发生一次FGC,这个频率就很高了,而每次FGC速度都是很慢的、性能很差。

(6)该案例应该如何进行JVM优化

通过上述这个案例,可以清楚看到:新生代和老年代应该如何配合使用,什么情况下会触发Young GC和Full GC,什么情况下会导致频繁的Young GC和Full GC。

如果要对这个系统进行优化:由于该系统是数据计算系统,每次YGC时都会有一批数据没计算完毕。所以按现有的内存模型,最大问题就是每次YGC后S区放不下存活对象。

所以可以对生产系统进行调整:增加新生代的内存比例,3G堆内存的2G给新生代,1G给老年代。这样S区大概就是200M,每次刚好能放得下YGC过后存活的对象。如下图示:

图片

只要每次YGC过后200M存活对象可以放进Survivor区域,那么等下次YGC时,这个S区的对象对应的计算任务早就结束可回收了。比如此时Eden区里1.6G空间被占满了,然后S1区里有200M上一轮YGC后存活的对象。如下图示:

图片

此时执行YGC后:就会把Eden区里1.6G对象回收掉,S1区里的200M对象也会回收掉,然后Eden区里剩余的200M存活对象会放入S2区。如下图示:

图片

以此类推,基本就很少有对象进入老年代了,老年代的对象也不会太多。这样就把生产系统老年代FGC的频率从几分钟一次降低到几小时一次,大幅度提升了系统的性能,避免了频繁FGC对系统运行的影响。

前面说过一个动态年龄判定升入老年代的规则:如果S区中的同龄对象大小超过S区内存的一半,就要直接升入老年代。

所以这里的优化仅仅是做一个示例说明而已,实际S区200M还是不够。但表达的是要增加S区大小,让YGC后的对象进入S区,避免进入老年代。

实际上为了避免触发动态年龄判定规则,把S区中的对象直接升入老年代,如果新生代内存有限,那么可以调整"-XX:SurvivorRatio=8"参数。比如降低Eden区的比例(默认80%),给两块S区更多的内存空间。让每次YGC后的对象进入S区,避免触发动态年龄规则把它们升入老年代。

(7)模拟程序用的JVM参数

把堆内存设置为200M,把年轻代设置为100M。然后Eden区80M,每块Survivor区10M,老年代100M。接着通过-XX:PretenureSizeThreshold,把大对象阈值修改为20M,避免模拟程序里分配的大对象直接进入老年代。​​​​​​​

 -XX:NewSize=104857600 -XX:MaxNewSize=104857600  -XX:InitialHeapSize=209715200 -XX:MaxHeapSize=209715200  -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15  -XX:PretenureSizeThreshold=20971520  -XX:+UseParNewGC -XX:+UseConcMarkSweepGC  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

(8)模拟程序​​​​​​​

public class Demo {    public static void main(String[] args) throws Exception {        Thread.sleep(30000);        while(true) {            loadData();        }    }        private static void loadData() throws Exception {        byte[] data = null;        for(int i = 0; i < 4; i++) {            data = new byte[10 * 1024 * 1024];        }        data = null;        byte[] data1 = new byte[10 * 1024 * 1024];        byte[] data2 = new byte[10 * 1024 * 1024];        byte[] data3 = new byte[10 * 1024 * 1024];        data3 = new byte[10 * 1024 * 1024];        Thread.sleep(1000);    }}

上面模拟程序的含义:

一.每秒钟都会执行一次loadData()方法。

二.loadData()首先会分配4个10M的数组,但都会马上成为垃圾。接着会有两个10M的数组会被变量data1和data2引用,必须存活,此时Eden区已经占用了六七十M的空间了。

三.接着是data3变量会依次指向两个10M的数组,1s内会触发YGC。

(9)基于jstat分析程序运行的状态

接下来启动程序后马上采用jstat监控其运行状态:​​​​​​​

$ jps517 652 RemoteMavenServer1213 Launcher1214 Demo1215 Jps$ jstat -gc 1214 1000 1000

可以看到如下的信息:

图片

下面分析这个JVM的运行状态。

一.首先看如下图示

图片

在最后一行可清晰看到,程序运行后,突然在一秒内就发生了一次YGC。因为按照上述的模拟代码,它一定会在一秒内触发一次YGC的。

YGC后,可以发现S1U中有536K的存活对象,这应该就是那些未知对象。然后明显看到在OU中多出30M左右的对象。因此可以确定,在这次YGC时,有30M的对象存活了。因为此时YGC后的存活对象在Survivor区放不下,所以直接进入老年代。

二.接着看如下图示

图片

上图中红圈部分:很明显每秒会发生一次YGC,每次会导致10M~30M的对象进入老年代。因每次YGC都存活这么多对象,但S区放不下,所以才直接进入老年代。

此时可以看到老年代的对象占用从30M一路升到60M。然后突然在60M后的下一秒,明显发生了一次FGC,对老年代进行回收,因为此时老年代重新变成30M了。

为什么会这样?因为老年代总共就100M左右,已占60M了。此时如果发生一次YGC,有30M存活对象要放入老年代,是明显不够的。此时必须要进行FGC,回收掉之前60M对象,然后再放入30M存活对象。

所以可以看到:按照模拟代码,几乎是每秒新增80M对象,每秒触发1次YGC。每次YGC后存活20M~30M的对象,老年代每秒新增20M~30M的对象,于是几乎每三秒触发一次老年代FGC。

这和上面的实时分析引擎的场景很类似:YGC太频繁,而且每次GC后存活对象太多,频繁进入老年代,从而频繁触发老年代的GC。

三.YGC和FGC的耗时如下图示

图片

可以发现:28次YGC,结果耗费了120毫秒,平均下来一次YGC要5毫秒左右。但是14次FGC才耗费24毫秒,平均下来一次FGC才耗费一两毫秒。这是为什么呢,为什么YGC比FGC还久?

因为按照上述模拟程序:

每次FGC都是由YGC触发的,所以是可能出现YGC比FGC还慢的情形的。因为YGC后存活对象太多要放入老年代,老年代内存不够才触发FGC。所以必须等FGC执行完毕,YGC才能把存活对象放入老年代,才算结束,从而导致YGC比FGC还慢。

(10)对JVM性能进行优化

接着按照前面介绍的思路对JVM进行优化,这次模拟程序最大的问题就是每次YGC过后存活对象太多,导致频繁进入老年代,频繁触发FGC。所以只需要调大新生代的内存空间,增加Survivor区的内存即可。调整为如下JVM参数:​​​​​​​

 -XX:NewSize=209715200 -XX:MaxNewSize=209715200  -XX:InitialHeapSize=314572800 -XX:MaxHeapSize=314572800  -XX:SurvivorRatio=2  -XX:MaxTenuringThreshold=15  -XX:PretenureSizeThreshold=20971520  -XX:+UseParNewGC -XX:+UseConcMarkSweepGC  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

把堆大小调大为300M,新生代给200M。同时-XX:SurvivorRatio=2表明,Eden:Survivor:Survivor的比例为2:1:1。所以Eden区是100M,每个Survivor区是50M,老年代也是100M。

接着用这个JVM参数运行程序,用jstat来监控其运行状态如下:

图片

从上图可见:每秒的YGC后,都会有20M左右的存活对象进入Survivor区。但由于每个S区都是50M,因此可以轻松容纳且不会触发动态年龄判定。

因此可以清晰看到每秒触发YGC后,几乎就没有对象会进入老年代。最终只有493K的对象进入了老年代;同时只有YGC,没有FGC。而且12次YGC才55毫秒,没有FGC干扰后,YGC的性能极高。

这样这个模拟程序就被成功优化了,同样的程序只调整了内存比例,就能大幅提升JVM性能,几乎消灭FGC。

6.问题汇总

问题一:

系统如何尽量减少Full GC?

(1)什么情况下发生FGC

一.YGC前:

情形一:新生代对象大小 > 老年代可用内存 && 没开通内存分配担保。

情形二:新生代对象大小 > 老年代可用内存 && 开通内存分配担保 && 历次新生代GC进入老年代大小平均值 > 老年代可用内存大小。

二.YGC后:

老年代放不下YGC后存活的对象。

(2)如何避免FGC

可以让每次YGC后,存活的对象尽量能放在S区,不要进入老年代。

一.调大Survivor区的大小

二.如果系统运算时间比较长导致对象年龄比较大,那么可以调大-XX:MaxTenuringThreshold参数,使得对象年龄大一些再进入老年代,这样也可以减少进入老年代的对象。

问题二:

在Tomcat中启动一个war,这是启动了一个JVM进程吗,还是多个?

答:Tomcat自己就是一个JVM进程,开发的war包不过就是一些类而已。Tomcat会将war包加载到自己的JVM进程里去执行类的代码逻辑。

问题三:

生产服务器的堆大小2G,其他都是默认。jmap看新生代Eden区和Survivor区的比例为8,按这比例,Survivor区应该是新生代的十分之一。但Eden区有680M,Survivor区只有10M,且每次YGC后Eden区和Survivor区的总大小都在变化,为什么呢?

答:因为设置了允许堆大小动态调整了,这个需要禁止掉的,就是让-Xmx和-Xms需要一样的值。

相关文章:

JVM实战—8.如何分析jstat统计来定位GC

大纲 1.使用jstat了解线上系统的JVM运行状况 2.使用jmap和jhat了解线上系统的对象分布 3.如何分析JVM运行状况并合理优化 4.使用jstat分析模拟的BI系统JVM运行情况 5.使用jstat分析模拟的计算系统JVM运行情况 6.问题汇总 1.使用jstat了解线上系统的JVM运行状况 (1)JVM的…...

halcon三维点云数据处理(五)创建代表工具和机器人底座的3D模型

目录 一、gen_robot_tool_and_base_object_model_3d 函数调用二、gen_arrow_object_model_3d 函数调用 首先说明一下这部分代码在find_box_3d这个例程中&#xff0c;非常好用的一个坐标系生成函数。 一、gen_robot_tool_and_base_object_model_3d 函数调用 RobotToolSize : 0.…...

React Router 向路由组件传state参数浏览器回退历史页面显示效果问题

昨天在看尚硅谷张天禹老师讲的 React教程p90&#xff0c;老师讲到 React路由的 replace模式和push模式&#xff0c;老师的演示效果与自己本地操作不太一样。 老师的效果&#xff1a;点击查看消息1&#xff0c;消息2&#xff0c;消息3 再点回退&#xff0c;可以依次查看到 消息…...

LabVIEW四旋翼飞行器姿态监测系统

四旋翼飞行器姿态监测系统是一个集成了高度、速度、俯仰角与滚转角数据采集与分析的系统&#xff0c;提高飞行器在复杂环境中的操作精确度与安全性。系统利用LabVIEW平台与硬件传感器相结合&#xff0c;实现实时数据处理与显示&#xff0c;有效地提升了四旋翼飞行器的监测与控制…...

HTML——66.单选框

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>单选框</title></head><body><!--input元素的type属性&#xff1a;(必须要有)--> <!--单选框:&#xff08;如所住省会&#xff0c;性别选择&…...

av1学习笔记(二):sequence_header_obu

av1学习笔记&#xff08;二&#xff09;&#xff1a;sequence_header_obu 目录 av1学习笔记&#xff08;二&#xff09;&#xff1a;sequence_header_obu1&#xff0c;图片的编解码方式1.1 seq_profile:1.2 still_picture1.3 reduced_still_picture_header1.4 编解码图像的宽高…...

嵌入式驱动开发详解8(阻塞/非阻塞/异步通信)

文章目录 前言阻塞非阻塞异步通知后续 前言 首先来回顾一下“中断”&#xff0c;中断是处理器提供的一种异步机制&#xff0c;我们配置好中断以后就 可以让处理器去处理其他的事情了&#xff0c;当中断发生以后会触发我们事先设置好的中断服务函数&#xff0c; 在中断服务函数…...

CSS进阶和SASS

目录 一、CSS进阶 1.1、CSS变量 1.2、CSS属性值的计算过程 1.3、做杯咖啡 1.4、下划线动画 1.5、CSS中的混合模式(Blending) 二、SASS 2.1、Sass的颜色函数 2.2、Sass的扩展(extend)和占位符(%)、混合(Mixin) 2.3、Sass的数学函数 2.4、Sass的模块化开发 2.5、Sass…...

求交错序列前N项和(PTA)C语言

本题要求编写程序&#xff0c;计算交错序列 1-2/33/5-4/75/9-6/11... 的前N项之和。 输入格式: 输入在一行中给出一个正整数N。 输出格式: 在一行中输出部分和的值&#xff0c;结果保留三位小数。 输入样例: 5输出样例: 0.917 代码&#xff1a; #include<stdio.h&g…...

【Delphi】创建COM服务器供浏览器(WebView)使用的操作步骤

首先创建一个VCL程序在程序的主界面放置WebView浏览器控件新增Automation Object&#xff08;ActiveX 页面中&#xff09;&#xff0c;重点&#xff0c;注意WebView只支持IDisptcher接口然后根据这个提示实现实现其函数功能在浏览器中众注册&#xff1a; Delphi程序 procedure …...

Spring Security(maven项目) 3.0.2.3版本

前言 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往复以至无穷&a…...

如何删除 Docker 中的悬虚镜像?

在 Docker 中&#xff0c;悬虚镜像&#xff08;Dangling Images&#xff09;是指那些没有 标签 且没有被任何容器使用的镜像。这些镜像通常是由于构建过程中生成的中间层镜像或未正确清理的镜像残留。删除悬虚镜像可以释放磁盘空间并保持 Docker 环境的整洁。 1. 列出悬虚镜像…...

【JMeter】配置元件Config Element

1.配置元件 作用&#xff1a; 通用接口配置&#xff0c;协议&#xff0c;IP, 端口等&#xff0c;减少重复工作量 元件的分类 HTTP Request Defaults即HTTP请求默认值 作用&#xff1a; 可以配置成通用请求行的信息&#xff0c;可复用 ​​​​​​​ ​​​​​​​ JDBC Co…...

【Vue】分享一个快速入门的前端框架以及如何搭建

先上效果图: 登录 菜单: 下载地址: 链接&#xff1a;https://pan.baidu.com/s/1m-ZlBARWU6_2n8jZil_RAQ 提取码&#xff1a;ui20 … 主要是可以自定义设置token,更改后端请求地址较为方便。 应用设置: 登录与token设置: 在这里设置不用登录,可以请求的接口: request.js i…...

搭建开源版Ceph分布式存储

系统&#xff1a;Rocky8.6 三台2H4G 三块10G的硬盘的虚拟机 node1 192.168.2.101 node2 192.168.2.102 node3 192.168.2.103 三台虚拟机环境准备 1、配置主机名和IP的映射关系 2、关闭selinux和firewalld防火墙 3、配置时间同步且所有节点chronyd服务开机自启 1、配置主机名和…...

运动相机拍摄的视频打不开怎么办

3-10 GoPro和大疆DJI运动相机的特点&#xff0c;小巧、高清、续航长、拍摄稳定&#xff0c;很多人会在一些重要场合用来拍摄视频&#xff0c;比如可以用来拿在手里拍摄快速运动中的人等等。 但是毕竟是电子产品&#xff0c;有时候是会出点问题的&#xff0c;比如意外断电、摔重…...

信号处理-消除趋势项

matlab 版本 python 版本 import numpy as np import matplotlib.pyplot as plt from matplotlib import rcParams# 设置中文字体 rcParams[font.sans-serif] [SimHei] # 设置默认字体为黑体 rcParams[axes.unicode_minus] False # 解决负号显示问题def compute_time(n, f…...

民宿酒店预订系统小程序+uniapp全开源+搭建教程

一.介绍 一.系统介绍 基于ThinkPHPuniappuView开发的多门店民宿酒店预订管理系统&#xff0c;快速部署属于自己民宿酒店的预订小程序&#xff0c;包含预订、退房、WIFI连接、吐槽、周边信息等功能。提供全部无加密源代码&#xff0c;支持私有化部署。 二.搭建环境 系统环境…...

Java(1)入门基础

1. Java简介 1.1 什么是Java Java 是一款由Sun Microsystems公司&#xff08;现为甲骨文公司Oracle Corporation的一部分&#xff09;的James Gosling及其团队在1995年发布的高级编程语言。同时&#xff0c;Java 是一种面向对象的语言&#xff0c;这意味着它允许开发者通过创…...

大风车excel:怎么把题库导入excel?题库导入excel

高效管理试题库&#xff1a;如何批量导入试题到 Excel&#xff1f; 在教育培训、学校管理以及在线学习平台中&#xff0c;试题库的管理是核心工作之一。如何快速、准确地将试题导入到 Excel 表格中&#xff0c;成为许多教育工作者和开发者的迫切需求。本文将围绕“题库导入 Ex…...

rabbitmq——岁月云实战笔记

1 rabbitmq设计 生产者并不是直接将消息投递到queue&#xff0c;而是发送给exchange&#xff0c;由exchange根据type的规则来选定投递的queue&#xff0c;这样消息设计在生产者和消费者就实现解耦。 rabbitmq会给没有type预定义一些exchage&#xff0c;而实际我们却应该使用自己…...

更改IP地址能提高網路速度嗎?

IP地址是由ISP分配給連接設備的唯一數字字串&#xff0c;允許設備與互聯網上的網站&#xff0c;伺服器和其他設備進行通信。因此&#xff0c;IP地址是訪問互聯網的基本條件之一。 IP 地址如何工作&#xff1f; 線上流覽時&#xff0c;網路請求都會使用IP地址從設備發送&#…...

Backend - C# asp .net core API(使用swagger)

目录 1. 安装Swagger组件 2. 查看swagger套件&#xff08;包&#xff09; 3. 配置Swagger中间件(Swagger服务) 4. 启动Swagger中间件 5. 接口文档配置为项目首页 6. swagger中的接口添加注释 1. 安装Swagger组件 工具栏“工具” > NuGet套件管理员 > 管理方案的NuGet套件…...

linux系统(ubuntu,uos等)连接鸿蒙next(mate60)设备

以前在linux上是用adb连接&#xff0c;现在升级 到了鸿蒙next&#xff0c;adb就不好用了。得用Hdc来了&#xff0c;在windows上安装了hisuit用的好好的&#xff0c;但是到了linux(ubuntu2204)下载安装了 下载中心 | 华为开发者联盟-HarmonyOS开发者官网&#xff0c;共建鸿蒙生…...

Unity-Mirror网络框架-从入门到精通之网络组件介绍

文章目录 前言网络组件Network AnimatorNetwork AuthenticatorNetwork DiscoveryNetwork IdentityNetwork ManagerNetwork Manager HUDNetwork Ping DisplayNetwork RigidbodyNetwork Room ManagerNetwork Room PlayerNetwork Start PositionNetwork StatisticsNetwork Transfo…...

nginx反向代理和负载均衡

nginx反向代理优点&#xff1a; 提高访问速度进行负载均衡保证后端服务安全&#xff08;将后端放到局域网&#xff09; 反向代理配置方式&#xff1a; nginx 负载均衡配置&#xff1a;...

Linux驱动开发学习准备(Linux内核源码添加到工程-Workspace)

Linux内核源码添加到VsCode工程 下载Linux-4.9.88源码&#xff1a; 没有处理同名文件的压缩包&#xff1a; https://pan.baidu.com/s/1yjIBXmxG9pwP0aOhW8VAVQ?pwde9cv 已把同名文件中以大写命名的文件加上_2后缀的压缩包&#xff1a; https://pan.baidu.com/s/1RIRRUllYFn2…...

2025.01.02 一月 | 充分地接受生活本身

一月 | 充分地接受生活本身 2025.01.02 吕方伊 课程导读 迎上去&#xff0c;抛却伤悲&#xff0c;放下消沉&#xff0c;坦然承载生命赋予的所有喜悦与不确定&#xff0c;真诚地拥抱最本真的自己。 英文原文 ▍今日一句 In order to understand the world, one has to turn…...

开源模型应用落地-qwen2-7b-instruct-LoRA微调合并-ms-swift-单机单卡-V100(十三)

一、前言 本篇文章将使用ms-swift去合并微调后的模型权重,通过阅读本文,您将能够更好地掌握这些关键技术,理解其中的关键技术要点,并应用于自己的项目中。 二、术语介绍 2.1. LoRA微调 LoRA (Low-Rank Adaptation) 用于微调大型语言模型 (LLM)。 是一种有效的自适应策略,…...

30、论文阅读:基于小波的傅里叶信息交互与频率扩散调整的水下图像恢复

Wavelet-based Fourier Information Interaction with Frequency Diffusion Adjustment for Underwater Image Restoration 摘要介绍相关工作水下图像增强扩散模型 论文方法整体架构离散小波变换与傅里叶变换频率初步增强Wide Transformer BlockSpatial-Frequency Fusion Block…...

uniapp H5页面实现懒加载

在 uniapp 中&#xff0c;要在小的 view 内实现列表懒加载&#xff0c;可以通过以下步骤来实现&#xff1a; 使用 scroll-view 组件来创建一个可滚动的区域。在 scroll-view内 部放置一个list组件&#xff0c;用于显示数据列表。监听 scroll-view 的滚动事件&#xff0c;当滚动…...

Linux(Centos 7.6)命令详解:mkdir

1.命令作用 如果目录还不存在&#xff0c;则创建目录(Create the DIRECTORY, if they do not already exist.) 2.命令语法 Usage: mkdir [OPTION]... DIRECTORY... 3.参数详解 OPTION: -m, --modeMODE&#xff0c;创建新目录同时设置权限模式-p, --parents&#xff0c;创…...

Freemarker模板进行判空

文章目录 freemarker判断对象是否为null使用 ?? 操作符使用 ?has_content 内建函数直接使用 ! 操作符取反 freemarker判断列表是否为空 freemarker判断对象是否为null 在 FreeMarker 模板引擎中&#xff0c;你可以使用内建的指令和条件判断来检测一个对象是否为 null。Free…...

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World 2025/1/3 14:06 百度&#xff1a;android studio helloworld android studio hello world kotlin helloword kotlin 串口 no run configurations added android studio no run configurations added 1、…...

Infineon PSoC 4 CapSense ModusToolbox IDE - 系统生态篇

本文档说明了 ModusToolbox 软体环境的 4 个层面&#xff0c;该环境为 CapSense 设备和生态系统提供支援。本文是 Infineon PSoC 4 CapSense ModusToolbox IDE-系统介绍的延伸篇 (Infineon PSoC 4 CapSense ModusToolbox IDE -系统介绍篇 - 大大通(简体站))。 什么是ModusToolb…...

Backend - EF Core(C# 操作数据库 DB)

目录 一、EF Core 1. 使用的ORM框架&#xff08;对象关系映射&#xff09; 2. EFCore 常见两种模式 3. EFCore 提供程序 二、 EF 操作数据库&#xff08;Code First&#xff09; 1. 下载NuGet插件 2.创建模型类文件 3.创建DBContext文件 4.Programs.cs文件 5.appsettings.Devel…...

ETL处理工具Kettle入门

1. Kettle简介 Kettle&#xff08;现已更名为Pentaho Data Integration&#xff0c;简称PDI&#xff09;是一个开源的ETL工具&#xff0c;能够进行数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;。它是…...

排序算法的实现(插入,希尔,选择,冒泡,堆排,快排)

目录 1.选择排序 2.冒泡排序 3.堆排序 4.插入排序 5.希尔排序 6.快排 6.1快排的优化 6.2快排&#xff08;双指针法&#xff09; 6.3快排&#xff08;非递归&#xff09; 7.归并排序 7.1归并非递归 8.计数排序 1.选择排序 对n个元素进行选择排序&#xff0c;我们可以…...

【微服务】5、服务保护 Sentinel

Sentinel学习内容概述 Sentinel简介与结构 Sentinel是Spring Cloud Alibaba的组件&#xff0c;由阿里巴巴开源&#xff0c;用于服务流量控制和保护。其内部核心库&#xff08;客户端&#xff09;包含限流、熔断等功能&#xff0c;微服务引入该库后只需配置规则。规则配置方式有…...

C 语言函数指针 (Pointers to Functions, Function Pointers)

C 语言函数指针 {Pointers to Functions, Function Pointers} 1. Pointers to Functions (函数指针)2. Function Pointers (函数指针)2.1. Declaring Function Pointers2.2. Assigning Function Pointers2.3. Calling Function Pointers 3. Jump Tables (转移表)References 1. …...

Mesa llvmpipe和softpipe对比

Mesa 后端性能分析&#xff1a;LLVM vs Software Pipe 当调试没有显卡的时候&#xff0c;可以使用cpu软件模拟的mesa-3d&#xff0c;后端采用kms_swrast_dri.so&#xff0c;发现管线使用llvmpipe的速度明显优于softpipe&#xff1b; 背景介绍 Mesa 是一个开源的图形库&#xf…...

【Spiffo】排障:VsCode报错“过程试图写入的管道不存在”(网络环境正常且地址正常的情况下依然出现)

摘要&#xff1a; VsCode使用remote-ssh报错nstall terminal quit with output: 过程试图写入的管道不存在&#xff0c;且输出类似下图的信息 一般来说这种情况更可能是网络环境出了问题&#xff0c;那连不上自不必多说&#xff0c;这里想讨论一种特殊情况。 有一种可能性&…...

Python图形界面(GUI)Tkinter笔记(二十一):Messagebox信息提示功能控件

messagebox 就像是 tkinter 库里的一个好帮手,它能帮你弹出各种各样的消息框给用户看。这些消息框可以告诉用户很多东西,比如提示、警告或者错误信息之类的。在 tkinter 库里,messagebox 这个模块有很多不同的函数,每个函数都能弹出一种特定的消息框。用这些函数,开发者可…...

KUKA机器人如何修改程序并下载到机器人控制器中?

KUKA机器人如何修改程序并下载到机器人控制器中? 如下图所示,首先将使用的网卡的IP地址设置为自动获得, 打开workvisual软件,点击搜索,正常情况下可以搜索到项目文件,选中后双击进入, 如下图所示,此时,workvisual会自动从机器人控制器中下载项目文件到电脑上,耐心等待…...

jmeter 中 BeanShell 预处理程序、JSR223后置处理程序使用示例

1. 各个组件如何新建的&#xff1f; 2. "http请求" 组件内容样例&#xff1a; "消息体数据" 源码&#xff1a; {"task_tag": "face_detect","image_type": "base64","extra_args": [{"model"…...

「Mac畅玩鸿蒙与硬件53」UI互动应用篇30 - 打卡提醒小应用

本篇教程将实现一个打卡提醒小应用&#xff0c;通过用户输入时间进行提醒设置&#xff0c;并展示实时提醒状态&#xff0c;实现提醒设置和取消等功能。 关键词 打卡提醒状态管理定时任务输入校验UI交互 一、功能说明 打卡提醒小应用包含以下功能&#xff1a; 提醒时间输入与…...

前端开发【插件】moment 基本使用详解【日期】

moment.js 是一个非常流行的 JavaScript 库&#xff0c;用于处理和操作日期与时间。它提供了丰富的 API 来处理各种日期、时间和格式化的操作。尽管随着 Date API 的增强&#xff0c;moment.js 被视为“过时”&#xff0c;并且推荐使用 date-fns 或 luxon 等库来替代&#xff0…...

Apache Celeborn 在B站的生产实践

背景介绍 Shuffle 演进 随着B站业务的飞速发展,数据规模呈指数级增长,计算集群也逐步从单机房扩展到多机房部署模式。多个业务线依托大数据平台驱动核心业务,大数据系统的高效性与稳定性成为公司业务发展的重要基石。如图1,目前在大数据基础架构下,我们主要采用 Spark、Fl…...

imageio 图片转mp4 保存mp4

目录 安装&#xff1a; imageio 图片转mp4 numpy 保存mp4 安装&#xff1a; FFMPEG: pip install imageio[ffmpeg] pyav: pip install imageio[pyav] imageio 图片转mp4 import glob import osimport cv2 import imageio from natsort import natsortedfrom PIL import …...

【C语言程序设计——选择结构程序设计】求阶跃函数的值(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 1. 选择结构基本概念 2. 主要语句类型​&#xff08;if、if-else、switch&#xff09; 3. 跃迁函数中变量的取值范围 4. 计算阶跃函数的值 编程要求 测试说明 通关代码 测试结果 任务描述 本关任务&#xff1a;输入x的值&#x…...