当前位置: 首页 > 后端技术 > Java

性能调优:CPU飙升FullGC问题处理

时间:2023-04-01 17:46:32 Java

1.前言最近产品在普通压测下没有实际问题,但是在实际环境中大数据的情况下(日数据量1.3亿左右)出现了一些系列的性能问题,所以一系列的情况当然,每个问题的解决方案都是不同的。这里只是记录个人的解题思路和步骤2.cpulevelcpulevel问题是由一系列的问题引起的。具体可以采用以下两种方案来处理:通过jdk提供的工具来处理通过arthas工具来处理,当然这种情况的前提是java进程的日志不报错,但是CPU占用高,如果有明显的错误,那么可以通过错误日志来诊断2.1arthasarthas是阿里巴巴开源的一款性能诊断工具,这里就不赘述了,直接上演示步骤如下:a.定位过程中CPU升高时,可以使用top命令查看,如下图:从图中可以看出CPU总使用率在2.8%左右,但是从具体进程,一些java进程大约占31%。当然,这绝对是正常的。这里只是一个例子。每种情况都不同。需要具体分析。当然是安装了。`htop命令,使用htop命令查看CPU各个核心的具体使用情况,如下:b.通过top命令定位后,可以使用具体的jps命令查看哪个进程占用最多,如下:可以发现是log-service目前占用量大,可以查看这个进程的问题C。启动arthas工具,如何使用这里不再赘述,如下:这里选择3-log-service进程,输入dashboard命令可以看到这个进程的监控信息如下:输入thread-n8命令查看CPU利用率最高的8个线程的日志。性能原因2.2影响从目前的优化过程来看,主要有两个因素(针对自己的系统)消耗资源,如下:正则表达式匹配数据库操作2.2.1正则A.问题出在JAVA中,正则表达式匹配使用NFA(NondeterministicFiniteAutomaton)进行匹配。当这种机制遇到不确定的字符串如+、*时,它会回溯。有些数据量很大,写的正则表达式不符合条件,就会不断回溯,特别会消耗CPU资源。这里,具体可以推荐别人的博客B,尽量少用不合格的正则模式解决问题。如果要使用正则模式,而且数据量大,尽量减少回溯底层尽量少用正则表达式实现的字符串拼接方式。比如String.format的所有前提都是建立在数据量大的基础上的。如果数据量较小,请忽略。您可以使用Jmeter等工具进行性能压测和验证。以上结果2.2.2数据库的原理比较简单,就是在一个周期内频繁操作数据库,如下:for(Stringss:aa){database_opertor()}这个方法会操作并发送一个数据库每次执行一个循环时连接,当数据量很大时,CPU资源会特别消耗。因此,建议将多个数据库连接改为一个或减少数据库连接方式,如下:查询一次,具体结果可以通过流处理实现批量新增、批量更新、批量删除一切前提是基于大量数据。数据量小请忽略。您可以使用Jmeter等工具进行性能压测来验证上述结果。同时记录一下FullGC的过程,具体过程如下:A.问题:测试人员通过Grafana监控到Java组件内存一直在上升,所以可能存在内存泄露的风险,如下图:经过一系列的分析,可能是JVM一直是FullGC引起的,所以为了验证这个想法,我们这里使用jvisualvm工具来分析一下(这个工具的使用会在文中详细说明)附文)。分析结果如下:发现老年代直接满了。当年老代满时,会频繁FullGC,但经过几轮GC后,还是满了。这时候就要分析一下为什么有充分的理由了。当我重启相应的组件时,heap中的old很快就满了,然后就可以频繁的FullGC了。jmap命令dump堆内存中的信息,如下:jmap-dump:file=filename.dumppid命令执行后,会生成一个filename.dump文件。通过jvisualvm工具分析这个文件,就知道堆中的对象信息是什么了。B、原因是通过jmap生成的dump文件分析,里面全是消费kafka的消息,也就是说old里面全是消费kafka的消息。old之所以满是因为消费kafka的消息太多了。第一次启动时,伊甸园区域已满。第一轮YGC结束后,大部分对象都没有处理完,直接进入s0区域。但是由于进入s0区的对象超过了s0区容量的一半,所以不再计算age,而是直接从s0到oldage,所以经过几次消费old一直处于满状态,然后有吃饱了GC,导致内存一直增长,最后出现内存泄漏C。其实要解决上面的问题,可见老年代里的空间其实很小。我的组件配置是-Xms6G-Xmx6G-Xmn5G,也就是说我在老年代的配置也只有1G的空间,所以解决方法如下:减少消耗kafka的个数,这样数据量就减少了并且不会出现频繁的FullGC。最后放弃了这个方案,因为如果消耗的数据量减少,性能就不会是最新的。到要求增加堆内存和增加旧空间大小的时候,也是放弃了。资源有限,调整切换G1垃圾收集器并不容易。当我使用JAVA8)时,这个垃圾收集器不同于默认的垃圾收集器。这个垃圾收集器没有物理生成。而是将一整块内存分成小块内存,并将内存标记为old,eden,s0,s1标记(具体细节自己理解),切换后的整体如下:完美解决了我的烦恼4.本章附件主要讲解Jvisualvm工具Start的使用,启动界面如下:4.2.简介在本地选择VisualVM可以在右边看到对应的JVM信息,同时在monitor中可以看到对应的区域信息,如下:还可以在线程中看到所有的线程情况,如如下:当然你也可以安装一个VisualGC插件来查询实时GC情况。安装步骤如4.3所述。plguins找到PluginsCenters,点击选择对应的jdk版本插件。我的jdk版本是java8_u40版本5,打开jvisualvm,找到tools-plugins,选择edit,填写对应的jdk插件地址,找到对应的插件安装即可。安装后可以点击VisualGC查看实时GC情况4.4Remote一般情况下jar部署在远程机器上,所以如果想查看实时GC可以通过远程查询联系。远程连接一般有两种方式:JMX连接这种连接方式Jstatd连接一般只能监控一个java服务。这个连接可以监控多个java服务。4.4.1JMX在远程Java服务上配置JMX连接如下:-Djava.rmi.server.hostname=serverip-Dcom.sun。management.jmxremote.port=jmx端口-Dcom.sun。management.jmxremote.authenticate=false-Dcom.sun.management.jmxremote.ssl=false在Jvisualvm上新建一个远程连接,并填写对应的主机名,填写如下:右击连接名,选择添加aJMX连接端口号与服务器的配置一致,然后点击连接。当然界面和之前local一样,如下:但是当你点击VisualGC的时候,显示如下结果:这个JVM不支持,所以可以使用Jstatd连接4.4。2jstatd在java服务中新建jstatd-all.policy,该文件可以放在任意位置,内容如下//注意:这里建议写JAVA_HOMEgrantcodebase的绝对路径"file:${JAVA_HOME}/lib/tools.jar"{permissionjava.security.AllPermission;};最后执行jstatd-J-Djava.security.policy=/opt/jstatd.all.policy-p12345命令,如果出现如下问题,安装如下配置最后发现权限不足,于是去授权,修改${JAVA_HOME}/jre/lib/security/java.policy文件权限java.io.FilePermission"/tmp/-","read";权限java.util.PropertyPermission"*","read";permissionjava.net.SocketPermission"*","connect,resolve,accept,listen";permissionjava.util.PropertyPermission"*","read";在文件中添加以上内容,如下:再次执行命令即可成功jstatd-J-Djava.security.policy=/opt/jstatd.all.policy-pport-J-Djava.rmi。server.logCalls=true-J-Djava.rmi.server.hostname=远程服务器ip连接jstatd,右键新建jstatd连接,最后可以看到所有组件信息,最后点击一个组件即可查看对应的GC情况