gdb调试core分析jvm(JNI)奔溃原因

本文分析了一个使用JNI调用C++ so库的Java服务在并发测试中突然崩溃的问题。通过gdb分析core dump文件,定位到C++源码中的异常抛出点,并最终通过调整异常处理方式解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  前几天服务(服务中使用了JNI调用了C++的so库)在并发测试几天后jvm突然奔溃,只在控制台打印出了一句话: terminate called after throwing an instance of '._0'。因为只根据这句话无法确定奔溃原因,于是查看linux系统日志,进入/var/log下,打开message文件,看到下面的话:

Sep 16 13:44:04 localhost abrt[37667]: Saved core dump of pid 4865 (/home/dear/smb/environments/jdk/bin/java) to /var/spool/abrt/ccpp-2017-09-16-13:27:35-4865 (6602772480 bytes)
Sep 16 13:44:04 localhost abrtd: Directory 'ccpp-2017-09-16-13:27:35-4865' creation detected
Sep 16 13:44:07 localhost abrtd: Executable '/home/dear/smb/environments/jdk/bin/java' doesn't belong to any package and ProcessUnpackaged is set to 'no'
Sep 16 13:44:07 localhost abrtd: 'post-create' on '/var/spool/abrt/ccpp-2017-09-16-13:27:35-4865' exited with 1
Sep 16 13:44:07 localhost abrtd: Deleting problem directory '/var/spool/abrt/ccpp-2017-09-16-13:27:35-4865'
是因为系统设置的问题,没有生成core dump文件,执行ulimit -c为0,于是修改系统配置,使其为unlimited,再次启动服务,期待问题浮现。果然在运行了几天后,又出现了 同样的问题,且生成了core dump文件。使用gdb对core文件进行分析,执行命令:gdb /home/dear/smb/environments/jdk/bin/java(java命令路径) core.4865(core dump文件名称)。然后键入bt命令,出现下列信息:

#0  0x0000003fe60323ae in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:67
#1  0x0000003fe60337f5 in abort () at abort.c:93
#2  0x00007f06386efcbd in __gnu_cxx::__verbose_terminate_handler() () at ../../../../gcc-4.9.2/libstdc++-v3/libsupc++/vterminate.cc:95
#3  0x00007f06386b2956 in __cxxabiv1::__terminate(void (*)()) () at ../../../../gcc-4.9.2/libstdc++-v3/libsupc++/eh_terminate.cc:47
#4  0x00007f06386b29a1 in std::terminate() () at ../../../../gcc-4.9.2/libstdc++-v3/libsupc++/eh_terminate.cc:57
#5  0x00007f06386b27c8 in __cxa_throw () at ../../../../gcc-4.9.2/libstdc++-v3/libsupc++/eh_throw.cc:87
#6  0x00007f06386a8f29 in ApLicenser::CutEffInfo (this=Unhandled dwarf expression opcode 0xf3
) at ap_license.cpp:64
#7  0x00007f06386a907e in ApLicenser::<lambda()>::operator()(void) const (__closure=0x7f0630290210) at ap_license.cpp:171
#8  0x00007f06386aa652 in ApLicenser::CatchInfo (this=Unhandled dwarf expression opcode 0xf3
) at ap_license.cpp:182
#9  0x00007f06386ab46e in ApLicenser::CheckLicense (this=0x7f0640a28700, mark=Unhandled dwarf expression opcode 0xf3
) at ap_license.cpp:269
#10 0x00007f06386abe0a in ApLicenser::CopyrightCheck (this=Unhandled dwarf expression opcode 0xf3
) at ap_license.cpp:414
#11 0x00007f06386a07b6 in ApHandler::CopyrightCheck (this=0x7f06407befd0, request_ip=..., user_num=-1, model_num=-1, verify_num=-1) at ../ap/ap_server.cpp:28
#12 0x00007f06386a2bdd in Java_com_dear_smb_bus_jni_NativeJNIInterface_checkLocalIpInfo (env=0x7f062ae279e8, clazz=Unhandled dwarf expression opcode 0xf3
) at vpr_model.cpp:387
#13 0x00007f0649a89d98 in ?? ()
#14 0xffffffffffffffff in ?? ()
#15 0xffffffffffffffff in ?? ()
#16 0x00000000afd36b70 in ?? ()
#17 0x00000000afd36b98 in ?? ()
#18 0x0000000000000000 in ?? ()
上面的信息列出了出错的native堆栈,因为gdb无法分析到java的层面,所以下面会打出??。通过上面的信息,可以看到,出错的C++源码部分,即,in ApLicenser::CutEffInfo (this=Unhandled dwarf expression opcode 0xf3) at ap_license.cpp:64,查看ap_license.cpp的64行,如下

void ApLicenser::CutEffInfo(const std::string str_order, std::string &result) {
    FILE* fp;  
    char   buf[1024];        
    bzero(buf, sizeof(buf)); 
    fp = popen( str_order.c_str(), "r" ); 
    if (fp == NULL) {
        throw AP_UNKNOWN_ERROR;
    }
    fread( buf, sizeof(char), sizeof(buf),  fp);   //64行
    pclose( fp ); 
    result = buf;
}
通过分析发现,此方法被调用时,上层使用catch (int err)进行捕捉,而AP_UNKNOWN_ERROR为枚举类型,改为throw (int)AP_READ_LICENSE_FAILED;(进行强制转换),问题解决。


<think>好的,我现在要解决用户的问题,用户提到他们的Java程序在运行时突然出现了hs_error_pid错误,也就是通常所说的JVM,生成了hs_err_pid<pid>.log文件。我需要先理解这个问题的原因,然后给出解决步骤。 首先,我得回忆一下JVM的常见原因。通常,这涉及到本地代码问题,比如JNI调用、内存不足、硬件或操作系统问题,或者JVM自身的bug。用户可能需要检查这些方面。 接下来,我需要分解解决问题的步骤。第一步应该是查看错误日志,因为里面会有关键信息,比如信号、问题发生的线程和堆栈跟踪。然后用户需要检查是否有堆栈溢出或OOM,不过OOM通常不会导致JVM,除非本地内存耗尽。 然后要考虑是否有JNI代码,如果有的话,可能是本地代码的问题,比如内存越界。这时候需要检查JNI代码,用Valgrind等工具排查。另外,硬件问题如内存损坏、磁盘故障或者CPU过热也可能导致崩,建议用户运行硬件诊断。 还有可能是操作系统的问题,比如资源限制或内核bug,这时候更新操作系统可能有帮助。JVM本身的bug虽然少见,但也不能排除,用户需要检查是否使用了过时的JDK版本,并考虑升级。 最后,如果用户无法自行解决,应该收集日志、核心转储和环境信息,联系Java支持或社区寻求帮助。整个过程中,保持回答结构清晰,分步骤引导用户排查问题,同时确保使用正确的LaTeX格式,比如行内公式用$...$,独立公式用$$...$$,但这个问题中可能不太需要数学公式,所以可能用不上。但需要确保中文回答,语法正确,并且符合用户可见层的指导方针。</think>当Java程序运行中突然出现`hs_error_pid`错误时,表明JVM发生了致命错误并崩,通常伴随生成名为`hs_err_pid<pid>.log`的日志文件。以下是逐步分析及解决方法: --- ### 1. **理解错误本质** - **触发原因**:JVM遇到无法恢复的错误(如内存访问冲突、堆栈溢出、本地代码问题等)。 - **日志文件**:生成的`hs_err_pid*.log`包含详细崩信息,如寄存器状态、线程堆栈、环境变量等。 --- ### 2. **关键排查步骤** #### **步骤1:查看错误日志** - **定位日志**:找到日志文件(默认在程序运行目录),检查以下部分: ```log # Problematic frame: [C 或 [jvm.dll] 等 # SIGSEGV (0xb) 或 SIGBUS 等信号 ``` - **信号类型**: - `SIGSEGV`:内存访问违规(如空指针、缓冲区溢出)。 - `SIGBUS`:内存对齐错误(常见于JNI代码)。 - `EXCEPTION_ACCESS_VIOLATION`(Windows):类似SIGSEGV。 #### **步骤2:检查代码与资源** - **堆栈溢出**:若日志中线程堆栈显示`java.lang.StackOverflowError`,需优化递归或循环调用。 - **JNI代码**:若使用了本地方法(JNI),检查C/C++代码的内存操作(如野指针、未初始化指针)。 - **资源泄漏**:检查文件句柄、数据库连接等是否未正确释放。 #### **步骤3:内存与硬件问题** - **物理内存不足**:本地内存耗尽可能导致崩,需监控内存使用(如`jstat -gc <pid>`)。 - **硬件故障**:运行内存测试工具(如`memtest86`)排除内存损坏。 - **过热或电源不稳**:检查服务器/设备温度及供电状态。 #### **步骤4:环境与依赖** - **JVM版本**:升级至最新稳定版JDK(如OpenJDK 17 LTS),避免已知Bug。 - **操作系统兼容性**:确保JVM与操作系统架构(32/64位)匹配。 - **三方库冲突**:检查依赖库(尤其本地库)版本兼容性。 #### **步骤5:调试工具辅助** - **核心转储分析**(Linux): ```bash gdb /path/to/java /path/to/core_dump bt # 查看崩时的调用栈 ``` - **Valgrind检查JNI**: ```bash valgrind --tool=memcheck java -jar YourApp.jar ``` --- ### 3. **典型解决方案** | 场景 | 解决方法 | |---------------------|-------------------------------------| | JNI代码内存越界 | 使用`AddressSanitizer`或重构本地代码 | | 堆外内存泄漏(如DirectByteBuffer) | 限制`-XX:MaxDirectMemorySize` | | JVM自身Bug | 降级或升级JDK版本 | | 操作系统资源限制 | 调整`ulimit`或内核参数 | --- ### 4. **示例日志片段分析** 假设日志中包含: ```log Problematic frame: C [libnative.so+0x1234] ``` 表明崩发生在动态库`libnative.so`的某个地址,需优先检查JNI代码或第三方本地库。 --- ### 5. **预防措施** - **压力测试**:使用JMeter模拟高负载场景。 - **启用JVM参数**: ```bash -XX:+ErrorFileToStderr # 输出错误日志到控制台 -XX:+CrashOnOutOfMemoryError # OOM时主动崩生成日志 ``` 若问题仍无法解决,建议将日志文件与完整环境信息提交至[JVM Bug数据库](https://bugs.openjdk.org/)或社区寻求帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值