java 调用 JNI 异常退出通过 core 定位

版权声明:转载请注明出处 https://blog.csdn.net/qiaojialin/article/details/81006114

场景描述

在 java 中存在jni调用,且不定期 java 进程全部消失,没有任何异常日志。通过分析 linux 的 core 文件来定位程序中哪里出了问题。

JNI 示例

首先构造一个带异常的 JNI,C函数中存在异常。

以下文件和命令都在同一个文件夹下执行

  • 创建文件:TestJni.java
import java.util.*;
public class TestJni
{
      public native void print();
      static
      {
           System.loadLibrary("TestJni");
      }

    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 3; i++) {
            Thread.sleep(1000);
            System.out.println("I am alive");
        }
        while(true) {
            new TestJni().print();
        }
    }

}
  • 生成 class 和 h 文件:
javac TestJni.java
javah TestJni
  • 创建文件:TestJni.c
#include "jni.h"
#include <stdio.h>
#include "TestJni.h"

JNIEXPORT void JNICALL
      Java_TestJni_print(JNIEnv *env,jobject obj)
{
printf("Hello World!\n");
        while(1) {
                int a[10000000];
        }
    return;
}
  • linux下需要以 lib 开头,so结尾
gcc -I /home/fit/jdk1.8/include -I /home/fit/jdk1.8/include/linux -fPIC -shared -o libTestJni.so TestJni.c
  • mac下可以用下边的命令
gcc -dynamiclib -I /Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/include TestJni.c -o libTestJni.jnilib

运行并产生 core 文件

  • 设置 core 文件大小为无限制
ulimit -u unlimited
  • 运行 java 进程(指定链接库地址为当前 so 所在目录)
java -Djava.library.path=./ TestJni

运行结果:

I am alive
I am alive
I am alive
Segmentation fault (core dumped)

此时就产生了系统 core 文件,可以分析了。这个文件的位置我是产生在了当前目录,网上很多说 echo 进 proc/sys/XXX 来设置 core 文件位置的我没成功过。

分析 core 文件

  • 用 gdb 打开 core 文件
gdb -core=core
  • 在 gdb 内部 输入
(gdb) info threads

输出

 Id   Target Id         Frame 
  20   LWP 18687         0x00007fb7554d37be in ?? ()
  19   LWP 18686         0x00007fb7554d3404 in ?? ()
  18   LWP 18685         0x00007fb7554d37be in ?? ()
  17   LWP 18682         0x00007fb7554d37be in ?? ()
  16   LWP 18676         0x00007fb7554d3404 in ?? ()
  15   LWP 18684         0x00007fb7554d37be in ?? ()
  14   LWP 18678         0x00007fb7554d37be in ?? ()
  13   LWP 18668         0x00007fb7554d065b in ?? ()
  12   LWP 18683         0x00007fb7554d37be in ?? ()
  11   LWP 18681         0x00007fb7554d5670 in ?? ()
  10   LWP 18680         0x00007fb7554d3404 in ?? ()
  9    LWP 18679         0x00007fb7554d3404 in ?? ()
  8    LWP 18677         0x00007fb7554d3404 in ?? ()
  7    LWP 18673         0x00007fb7554d3404 in ?? ()
  6    LWP 18672         0x00007fb7554d3404 in ?? ()
  5    LWP 18675         0x00007fb7554d3404 in ?? ()
  4    LWP 18674         0x00007fb7554d3404 in ?? ()
  3    LWP 18670         0x00007fb7554d3404 in ?? ()
  2    LWP 18671         0x00007fb7554d3404 in ?? ()
* 1    LWP 18669         0x00007fb7249736d0 in ?? ()        

最后带 * 的那一行就是挂掉的线程号 18669,记住这个号。

在 gdb 中输入 quit 退出

(gdb) quit

jstack 找日志

  • 指定 java 位置和 core 文件
jstack /home/fit/jdk1.8/bin/java core |more
  • /搜索 线程号 18669
/18669 回车
  • 找到了日志
/18669
...skipping
Thread 18669: (state = IN_NATIVE)
 - TestJni.print() @bci=0 (Interpreted frame)    ## 在这里!!!!!!!!
 - TestJni.main(java.lang.String[]) @bci=34, line=19 (Interpreted frame)

显示是 TestJni 类的 main() 中的 print() 造成的程序退出

欢迎关注个人公众号:数据库漫游指南

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qiaojialin/article/details/81006114