记一次jvm crash 的崩溃分析

程序崩溃日志开头如下:主要关键字G1ParCopyClosure

# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007fe629cb5, pid=113675, tid=113703
#
# JRE version: Java(TM) SE Runtime Environment (14.0+36) (build 14+36-1461)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (14+36-1461, mixed mode, sharing, tiered, compressed oops, g1 gc, linux-amd64)
# Problematic frame:
# V  [libjvm.so+0x6d0afa]  G1ParCopyClosure<(G1Barrier)0,(G1Mark)0>::do_oop(unsigned int*)+0x5a
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t e %P %I %h" (or dumping to /home/***/xxweb/core.113675)
#
# If you would like to submit a bug report, please visit:

这里一个重要信息是“SIGSEGV (0xb)”表示jvm crash时正在执行jni代码,而不是在执行java或者jvm的代码,如果没有在应用程序里手动调用jni代码,那么很可能是JIT动态编译时导致的该错误。

PS:除了“SIGSEGV(0xb)”以外,常见的描述还有“EXCEPTION_ACCESS_VIOLATION”,该描述表示jvm crash时正在执行jvm自身的代码,这往往是因为jvm的bug导致的crash;另一种常见的描述是“EXCEPTION_STACK_OVERFLOW”,该描述表示这是个栈溢出导致的错误,这往往是应用程序中存在深层递归导致的。

还有一个重要信息是:

# Problematic frame:
# V  [libjvm.so+0x6d0afa]  G1ParCopyClosure<(G1Barrier)0,(G1Mark)0>::do_oop(unsigned int*)+0x5a

这表示出现crash时jvm正在执行的操作“V”表示是 VM框架

还有可能是“C”、“j”、“V”、“v”,它们分别表示:

  • C: Native C frame
  • j: Interpreted Java frame
  • V: VMframe
  • v: VMgenerated stub frame
  • J: Other frame types, including compiled Java frames

 其中G1ParCopyClosure很明显,也可以由此判断出是在垃圾回收时出了问题。还有个关键日志

---------------  T H R E A D  ---------------

这段日志是Java虚拟机(JVM)在发生崩溃或异常时生成的线程堆栈跟踪信息。从日志中,我们可以提取以下关键信息:

  1. 当前线程

    • 当前线程是一个名为"GC Thread#2"的垃圾收集(GC)任务线程,其ID为113703。
    • 线程的栈内存范围为[0x00007fe5cc4f0000,0x00007fe5cc5f0000],当前栈指针(sp)位置为0x00007fe5cc5ed750,栈上剩余空间为1013k。
  2. 原生帧(Native frames)

    • 这部分列出了崩溃发生时线程的调用栈,从JVM的底层开始,向上直到发生崩溃的位置。
    • 可以看到,崩溃发生在G1ParCopyClosure<(G1Barrier)0, (G1Mark)0>::do_oop(unsigned int*)这个函数中,这个函数属于G1垃圾收集器的一部分,用于并行复制对象。
    • G1ParCopyClosureOopMapSetframeJavaThread等函数和类都与JVM的垃圾收集机制相关。
  3. Java帧(Java frames)

    • 这部分显示了崩溃发生时Java层面的调用栈。
    • 崩溃发生在cn.hutool.core.util.ReflectUtil.invoke方法中,该方法来自hutool这个Java工具包。
    • 接下来是cn.hutool.core.bean.PropDesc.getValue方法,与获取JavaBean属性的值有关。
    • 接着是cn.hutool.core.bean.copier.BeanCopier.valueProviderToBean方法,这是用于将值提供者(ValueProvider)中的值复制到JavaBean对象中的操作。
    • 最后是Lambda表达式accept方法,这通常是用于处理集合中元素的函数式接口方法。

分析

  • 崩溃发生在G1垃圾收集器的并行复制阶段,这通常与内存管理或垃圾收集器的配置有关。
  • ReflectUtil.invoke方法的调用可能与反射操作有关,这可能会增加内存使用和垃圾收集的压力。
  • PropDesc.getValueBeanCopier.valueProviderToBean可能涉及JavaBean属性的访问和复制,这也可能涉及大量的内存分配和对象创建。
  • ReflectUtil.invoke方法的调用可能触发了大量的对象创建或内存分配,增加了垃圾收集的压力。
  • 如果ReflectUtil.invoke方法被频繁调用,或者调用的方法体很大,那么它可能会占用大量的栈空间,导致栈溢出或内存问题。

 解决

结合代码确实发现有循环中调用BeanUtil.copyPropertie,也确实会产生大量对象,那么具体是将这段代码修改还是优化G1配置则可以经过观察确定。

BeanUtil.copyPropertie替换掉吧

G1的配置可以尝试设置ParallelGCThreads和ConcGCThreads 

java -XX:+PrintFlagsFinal -version 命令查看JVM默认参数具体可以grep查看

三种GC下,ParallelGCThreads默认值都是CPU线程数

ConcGCThreads 参数设置公式:

(参考https://download.youkuaiyun.com/blog/column/10400618/121548143

  1. 并行Parallel GC下:ConcGCThreads = 0
  2. CMS GC下,ConcGCThreads=(ParallelGCThreads+3)/4下取整
  3. G1 GC下,ConcGCThreads=ParallelGCThreads/4四舍五入

以上是分析过程希望对你有帮助。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值