纯干货:大对象导致FullGC频繁的原因及实践思路

2024-01-31 22:20

本文主要是介绍纯干货:大对象导致FullGC频繁的原因及实践思路,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天在检查线上环境的时候,发现了在2分钟内出现了2次FullGC。
虽然对线上功能影响不是很大,但还是想一探究竟。

线上监控得到的信息:
CAT监控
可以看到从短时间内有了2次GC,从13次直接飙到15次。

然后看了下老年代的堆情况:
在这里插入图片描述
可以看到这两次分别从620M直接下降到了400M然后又下降到了200M的样子。

脑海中的直觉应该是出现了大对象的感觉,因为老年代的堆是650M。达到620M触发GC,可能是堆空间不足,对象分配不进去,触发了1次GC,清理了200M,这个没什么问题,但是同一时刻又触发了一次GC,又清理了一遍,这个就是有问题了。

但是这个不是什么内存溢出啊啥的,dump不到大对象呀。事故现场已经被清理完了这可咋整喔 。

这个时候我在想,要是Full GC之前能够得到hprof文件就好了。

但其实JVM早就提供了这些参数了。

开启GC参数

# 1. 查看可实时配置的GC参数
java -XX:+PrintFlagsFinal -version | grep manageable
# 2. 查看服务进程编号
jps
# 3. 在full gc前开启dump文件 +表示开启 -表示关闭。 18881 代表应用进程编号
jinfo -flag +HeapDumpBeforeFullGC 18881
jinfo -flag HeapDumpPath=/elab/spring-boot/logs/dump_file 18881
# 查看配置是否生效
jinfo -flag HeapDumpPath 18881# 查看当前应用的jvm配置
jinfo -flags 18881

通过第一个命令可以不重启应用实时开启的参数:

java -XX:+PrintFlagsFinal -version | grep manageableintx CMSAbortablePrecleanWaitMillis            = 100                                 {manageable}intx CMSTriggerInterval                        = -1                                  {manageable}intx CMSWaitDuration                           = 2000                                {manageable}bool HeapDumpAfterFullGC                       = false                               {manageable}bool HeapDumpBeforeFullGC                      = false                               {manageable}bool HeapDumpOnOutOfMemoryError                = false                               {manageable}ccstr HeapDumpPath                              =                                     {manageable}uintx MaxHeapFreeRatio                          = 100                                 {manageable}uintx MinHeapFreeRatio                          = 0                                   {manageable}bool PrintClassHistogram                       = false                               {manageable}bool PrintClassHistogramAfterFullGC            = false                               {manageable}bool PrintClassHistogramBeforeFullGC           = false                               {manageable}bool PrintConcurrentLocks                      = false                               {manageable}bool PrintGC                                   = false                               {manageable}bool PrintGCDateStamps                         = false                               {manageable}bool PrintGCDetails                            = false                               {manageable}bool PrintGCID                                 = false                               {manageable}bool PrintGCTimeStamps                         = false                               {manageable}

这里我们关注其中几个参数:

  • PrintClassHistogramBeforeFullGC
    • 这个参数是说在full gc前会将内存中的对象以日志的形式输出,但是很多大对象都是些byte啊啥的,你压根不知道是那个对象引用的。
  • HeapDumpBeforeFullGC
    • 这个参数就是full gc前将hprof文件保存下来
  • HeapDumpPath
    • dump下来的hprof文件存放位置

通过上述操作,在应用下一次full gc的时候便会保存hprof文件文件

分析hprof文件

通过上述命令保存下来的文件大概有1.3G,有点大。

下载下来会比较麻烦。

这里通过MAT的linux的工具直接在服务器上进行分析。

MAT分析工具
从这个网站上下载Linux (x86_64/GTK+)

如何使用?
cd mat
./ParseHeapDump.sh /elab/spring-boot/dump.hprof  org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components# 预计五分钟之后出结果,查看结果就到hprof所在的位置

/elab/spring-boot/dump.hprof : 位置

-rw-r--r-- 1 root root 8.3M Mar 20 10:15 java_pid18881.a2s.index
-rw-r--r-- 1 root root  13M Mar 20 10:15 java_pid18881.domIn.index
-rw-r--r-- 1 root root  37M Mar 20 10:15 java_pid18881.domOut.index
-rw------- 1 root root 1.3G Mar 20 10:01 java_pid18881.hprof
-rw-r--r-- 1 root root 295K Mar 20 10:16 java_pid18881.i2sv2.index
-rw-r--r-- 1 root root  33M Mar 20 10:15 java_pid18881.idx.index
-rw-r--r-- 1 root root  50M Mar 20 10:15 java_pid18881.inbound.index
-rw-r--r-- 1 root root 7.2M Mar 20 10:15 java_pid18881.index
-rw-r--r-- 1 root root 100K Mar 20 10:15 java_pid18881_Leak_Suspects.zip
-rw-r--r-- 1 root root  12M Mar 20 10:15 java_pid18881.o2c.index
-rw-r--r-- 1 root root  33M Mar 20 10:15 java_pid18881.o2hprof.index
-rw-r--r-- 1 root root  28M Mar 20 10:15 java_pid18881.o2ret.index
-rw-r--r-- 1 root root  49M Mar 20 10:15 java_pid18881.outbound.index
-rw-r--r-- 1 root root  82K Mar 20 10:15 java_pid18881_System_Overview.zip
-rw-r--r-- 1 root root 356K Mar 20 10:15 java_pid18881.threads
-rw-r--r-- 1 root root 256K Mar 20 10:16 java_pid18881_Top_Components.zip

一共会有这么些东西,你只要关注3个*.zip包就行了。
把这3个下载到本地,里面是html文件,打开就是结果。
主要关注 : java_pid18881_Leak_Suspects.zip 这个文件
打开结果:
在这里插入图片描述

我们看到有一个216M的大对象出现了。

然后点击链接进去看是那个线程造成的.
在这里插入图片描述
我们这里就找到了具体业务触发的方法了。

我这里就不贴具体的方法了,最根本的原因是一个图片压缩的功能造成的

Thumbnails.of(file.getInputStream()).scale(0.1f).toFile(outputImg);

如果客户端上传的图片太大,会通过这个方法进行压缩。由于对象本身会很大的话,很容易触发Full GC。

然后我根据这个时间点去监控系统中查询该URL的方法日志的时候,也发现了一个超过9秒的请求,根据方法执行的时间链路基本上也就确定了就是上述代码造成的。

好了,具体过程就是这样。

总结一下 :

  1. 通过开启JVM的参数,在full GC前保留一份hprof文件。
  2. 通过MAT的linux工具直接在服务器上分析,避免文件过大下载下来太慢。
  3. 然后查看结果页来找到具体的大对象

如果你还有什么更好的排查思路以及工具欢迎交流。

这篇关于纯干货:大对象导致FullGC频繁的原因及实践思路的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/665238

相关文章

SQLite3命令行工具最佳实践指南

《SQLite3命令行工具最佳实践指南》SQLite3是轻量级嵌入式数据库,无需服务器支持,具备ACID事务与跨平台特性,适用于小型项目和学习,sqlite3.exe作为命令行工具,支持SQL执行、数... 目录1. SQLite3简介和特点2. sqlite3.exe使用概述2.1 sqlite3.exe

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

Springboot整合Redis主从实践

《Springboot整合Redis主从实践》:本文主要介绍Springboot整合Redis主从的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言原配置现配置测试LettuceConnectionFactory.setShareNativeConnect

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

《MyBatisPlus中update_time字段自动填充失效的原因分析及解决方案(最新整理)》在使用MyBatisPlus时,通常我们会在数据库表中设置create_time和update... 目录前言一、问题现象二、原因分析三、总结:常见原因与解决方法对照表四、推荐写法前言在使用 MyBATis

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

MySQL版本问题导致项目无法启动问题的解决方案

《MySQL版本问题导致项目无法启动问题的解决方案》本文记录了一次因MySQL版本不一致导致项目启动失败的经历,详细解析了连接错误的原因,并提供了两种解决方案:调整连接字符串禁用SSL或统一MySQL... 目录本地项目启动报错报错原因:解决方案第一个:第二种:容器启动mysql的坑两种修改时区的方法:本地

java中Optional的核心用法和最佳实践

《java中Optional的核心用法和最佳实践》Java8中Optional用于处理可能为null的值,减少空指针异常,:本文主要介绍java中Optional核心用法和最佳实践的相关资料,文中... 目录前言1. 创建 Optional 对象1.1 常规创建方式2. 访问 Optional 中的值2.1

Nginx Location映射规则总结归纳与最佳实践

《NginxLocation映射规则总结归纳与最佳实践》Nginx的location指令是配置请求路由的核心机制,其匹配规则直接影响请求的处理流程,下面给大家介绍NginxLocation映射规则... 目录一、Location匹配规则与优先级1. 匹配模式2. 优先级顺序3. 匹配示例二、Proxy_pa

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可