我正在寻找 JNA 出现奇怪问题的可能原因。我有一个 Java 应用程序,它使用一堆共享库,这些库都是我和我的同事自制的、编程的。我们使用 JNA 5.x 将 SO 中的 C 函数公开给 java。 (Linux 上的 Java 11.20)。我们使用 Gradle 7.4 构建 Java 代码,使用 cmake 将 C 代码编译成 SO。
问题是,当我使用 Gradle 构建项目并按顺序执行所有测试时,JVM 崩溃了。在我添加了一个测试某个库中的函数的单元测试之后,这种情况就开始发生了。我收到以下错误:
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007fa08abac0ef, pid=129694, tid=129697
#
# JRE version: OpenJDK Runtime Environment (Red_Hat-11.0.20.0.8-1.fc38) (11.0.20+8) (build 11.0.20+8)
# Java VM: OpenJDK 64-Bit Server VM (Red_Hat-11.0.20.0.8-1.fc38) (11.0.20+8, mixed mode, sharing, tiered, compressed oops, g1 gc, linux-amd64)
# Problematic frame:
# C [libc.so.6+0x990ef]
所以它一定是这个库中的东西,或者是我从中调用特定函数的方式。让这个问题对我来说很难的是,当我只执行一个单元测试时,它是绿色的。 JVM 没有问题,结果符合预期。
GDB 也没有给我任何真正的线索,或者至少我无法解释它们。当我调试gradle进程时,我可以看到函数调用是成功的,因为返回值中有正确的结果,但是执行函数返回后,我在libc中遇到了问题。
Thread 2 "Test worker" received signal SIGSEGV, Segmentation fault.
0x00007fe3948420ef in unlink_chunk (p=p@entry=0x7fe38caedbe0, av=0x7fe38c000030) at malloc.c:1604
1604 if (chunksize (p) != prev_size (next_chunk (p)))
(gdb) c
Continuing.
Thread 2 "Test worker" received signal SIGILL, Illegal instruction.
futex_wait (private=0, expected=2, futex_word=0x7fe38c000030) at ../sysdeps/nptl/futex-internal.h:146
Downloading source file /usr/src/debug/glibc-2.37-10.fc38.x86_64/nptl/../sysdeps/nptl/futex-internal.h
146 int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
(gdb) n
0x00007fe394317d10 in crash_handler(int, siginfo_t*, void*) () from /usr/lib/jvm/java-11-openjdk-11.0.20.0.8-1.fc38.x86_64/lib/server/libjvm.so
(gdb) n
Single stepping until exit from function _ZL13crash_handleriP9siginfo_tPv,
which has no line number information.
Thread 20 "VM Periodic Tas" received signal SIGABRT, Aborted.
[Switching to Thread 0x7fe365fff6c0 (LWP 131363)]
__pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
44 return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0;
(gdb) n
Couldn't get registers: No such process.
(gdb) [Thread 0x7fe36476d6c0 (LWP 131400) exited]
非常感谢任何在哪里查找以及如何继续查找问题原因的想法。
在没有看到 Java 实现的情况下,很难准确地确定原因,但是来自本机端的错误清楚地表明您正在处理不属于您的本机内存:
Thread 2 "Test worker" received signal SIGSEGV, Segmentation fault.
0x00007fe3948420ef in unlink_chunk (p=p@entry=0x7fe38caedbe0, av=0x7fe38c000030) at malloc.c:1604
1604 if (chunksize (p) != prev_size (next_chunk (p)))
指向这段代码:
* Take a chunk off a bin list. */
static void
unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");
这些宏最终会调用
(p)->mchunk_size
和 (p)->mchunk_prev_size
;但 Java 进程很可能不拥有与 p
相关的内存,因此崩溃。可能您已经释放了它,或者可能 p
来自一些不应该释放的字节组。
对此进行调试需要遵循与本机函数调用相关的 Java/JNA 端和本机端内存分配。如果在Java端分配,则必须在Java端释放;如果您在本机端分配,则可以使用适当的本机 API 来释放该分配。您可能两者都在做。
JNA 的一个常见问题是,包含内存分配的对象可能在某些时候无法访问,并且有资格进行垃圾回收;本机内存作为该过程的一部分被释放。确保保留对与该内存分配关联的任何 Java 对象的强引用(
Structure
或 Memory
等)。
另一个潜在的候选者是堆栈损坏,即您从一组无意的字节中发送了指针地址;当本机函数调用中的类型映射不正确时,可能会发生这种情况。这可能是这里的罪魁祸首,释放内存的调用自行工作,但使用它运行其他测试可能会损坏堆栈。