我有一个java程序调用JNI函数来对字节数组执行计算,返回一个长数组。该程序在 Oracle JDK 1.8.0_221 上运行。为了测试吞吐量,我使用 JMH 1.21
在大小为 64M 的同一字节数组上运行代码进行多次迭代java端代码如下
public long[] compute(byte[] input, int resultSize) {
long[] result = new long[resultSize];
nativeCompute(input, result);
return result;
}
native void nativeCompute(byte[] input, long[] output);
JNI 代码只是从 Java 数组获取指针并将其传递给计算函数
JNIEXPORT void JNICALL Java_NativeCompute_nativeCompute
(JNIEnv *env, jobject self, jbyteArray input, jlongArray result) {
// uint8_t *data = (uint8_t *) env->GetPrimitiveArrayCritical(input, 0);
// uint64_t *localres = (uint64_t *) env->GetPrimitiveArrayCritical(result, 0);
jbyte *data = env->GetByteArrayElements(input, 0);
jlong *localres = env->GetLongArrayElements(result, 0);
compute((uint8_t *) data, (uint64_t *) localres);
// env->ReleasePrimitiveArrayCritical(input, data, JNI_ABORT);
// env->ReleasePrimitiveArrayCritical(result, localres, JNI_COMMIT);
env->ReleaseLongArrayElements(result, localres, JNI_COMMIT);
env->ReleaseByteArrayElements(input, data, JNI_ABORT);
}
我使用
GetByteArrayElements
和GetLongArrayElements
从java数组中获取指针,程序运行良好。
为了避免数组复制,我尝试切换到
GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical
,如注释掉的代码中所示。性能提高了 15%,但 JVM 在运行一些(30-150)次迭代后崩溃了。错误日志如下
# Run progress: 0.00% complete, ETA 00:12:30
# Fork: 1 of 5
# Warmup Iteration 1: 7.436 ops/s
# Warmup Iteration 2: #
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007fe4b14ca554, pid=14023, tid=0x00007fe49baa2700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_221-b11) (build 1.8.0_221-b11)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# V [libjvm.so+0x987554][thread 140620183496448 also had an error]
[thread 140619839837952 also had an error]
oopDesc* PSPromotionManager::copy_to_survivor_space<false>(oopDesc*)+0x204
#
当我切换回
Get<XX>ArrayElements
时,这个错误就消失了。但我仍然希望获得 15% 的性能提升GetPrimitiveArrayCritical
。
如有任何建议,我们将不胜感激。谢谢!
谢谢大家的建议!我已按照您的建议进行了尝试,这里有一些更新。
我添加了代码来检查指针是否为 NULL。它们不为空。
该错误仅发生在JMH环境中。当我独立运行代码并重复 2000 次迭代时,没有错误。
我注释掉了计算,错误就消失了。看起来崩溃是由数组溢出引起的。我仔细检查了代码,发现 当输入元素的数量是 64 的倍数时,我的代码将意外地跨越结果数组的边界。我修复了该错误,现在错误消失了。我应该仔细检查代码以避免这种愚蠢的错误。
虽然还不清楚为什么错误只发生在JMH环境中,但我想我们已经找到了根本原因,谢谢大家的帮助!
在我看来,你的计算内容中出现了一些可疑的情况。如果我使用与您相同的方法进行非常简单的测试,我不会遇到任何问题。
此外,非常重要的是,
compute
不做任何与 JNI
相关的事情。 PrimitiveArray
附带价格。
当然,对于同一函数的 100 次循环(如下所示:https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo053),您可以看到加速方面的明显好处
Access via PrimitiveArray
14.64 real 14.55 user 0.12 sys
Access via ArrayElements
51.55 real 35.94 user 15.13 sys
快两倍半。无论如何,因为
PrimitiveArray
是有成本的
调用GetPrimitiveArrayCritical后,本机代码 在调用之前不应运行很长一段时间 释放PrimitiveArrayCritical。
我会重新考虑使用
PrimitiveArray
来表示这种数组大小是否是你真正想要的。
更新
起来!看起来在我准备答案的时候就解决了;)
我最近也遇到了这种错误,但不幸的是,我们的项目太大了,而且还依赖于许多其他项目,我们无法确定哪些方法完成了
array overflow
的工作。你有什么建议吗?我们拥有的只是 jvm 崩溃文件,例如:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007eff7b312b04, pid=100200, tid=0x00007eff62bfa700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_261-b12) (build 1.8.0_261-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.261-b12 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# V [libjvm.so+0x98eb04] oopDesc* PSPromotionManager::copy_to_survivor_space<false>(oopDesc*)+0x204
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
#
--------------- T H R E A D ---------------
Current thread (0x00007eff7402f800): GCTaskThread [stack: 0x00007eff62afb000,0x00007eff62bfb000] [id=100281]
siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x00000063f9000000
Registers:
RAX=0x00000063f9000000, RBX=0x00000007ba484dd8, RCX=0x0000000000000004, RDX=0x00007eff7b95c5f8
RSP=0x00007eff62bf9c60, RBP=0x00007eff62bf9cf0, RSI=0x00000007ba484dd8, RDI=0x00007eff74083240
R8 =0x00000063f9000000, R9 =0x0000000493436a5d, R10=0x00007eff7b928ea0, R11=0x0000000000000018
R12=0x00007eff74083240, R13=0x0000000000000001, R14=0x00007eff740832a8, R15=0x0000000000000004
RIP=0x00007eff7b312b04, EFLAGS=0x0000000000010206, CSGSFS=0x0000000000000033, ERR=0x0000000000000004
TRAPNO=0x000000000000000e
Top of Stack: (sp=0x00007eff62bf9c60)
0x00007eff62bf9c60: 00007eff62bf9cf0 0000000000000400
0x00007eff62bf9c70: 0000000000000038 0000000600000007
0x00007eff62bf9c80: 0000000000000038 0000000000000009
0x00007eff62bf9c90: 00000004934369f8 0000000000000400
0x00007eff62bf9ca0: 0000000000000000 00007eff7b94d712
0x00007eff62bf9cb0: 00007eff62bf9d40 00007eff7b311921
0x00007eff62bf9cc0: 00007efe3009fc20 00007eff74083240
0x00007eff62bf9cd0: 0000000493436a5c 0000000000000001
0x00007eff62bf9ce0: 00007eff740832a8 00007eff7b928eb0
0x00007eff62bf9cf0: 00007eff62bf9d40 00007eff7b312548
0x00007eff62bf9d00: 0ac02d33000076e9 0ac02d34000076e9
0x00007eff62bf9d10: 00007eff62bf9d50 00007eff740837d0
0x00007eff62bf9d20: 0000000000000006 00007eff62bf9d7c
0x00007eff62bf9d30: 0000000000000000 0000000000000005
0x00007eff62bf9d40: 00007eff62bf9dc0 00007eff7b316b2e
0x00007eff62bf9d50: 00007eff74083240 00007efe3009fe00
0x00007eff62bf9d60: 00007eff74083240 0000000000000008
0x00007eff62bf9d70: 00007eff74023f70 0a4bb56a74023f70
0x00007eff62bf9d80: 0a36c37c0000d898 0a36c37c0000d899
0x00007eff62bf9d90: 00007eff62bf9dc0 0000000000000001
0x00007eff62bf9da0: 00007efe3009fe00 00007eff7402f800
0x00007eff62bf9db0: 0000000000000000 00007eff7b94d4a8
0x00007eff62bf9dc0: 00007eff62bf9ed0 00007eff7af4a266
0x00007eff62bf9dd0: 00000000000003d8 00007eff7402fc10
0x00007eff62bf9de0: 00007eff74030038 00007eff7402fc60
0x00007eff62bf9df0: 00007eff62bf9e60 00007eff7402fc50
0x00007eff62bf9e00: 00007eff62bf9e18 00007eff7b4f7eba
0x00007eff62bf9e10: 0000000000000000 0000000000000000
0x00007eff62bf9e20: 00007eff7402f800 00007eff74030040
0x00007eff62bf9e30: 00007eff74030080 00007eff74030090
0x00007eff62bf9e40: 00007eff74030168 00000000000000d8
0x00007eff62bf9e50: 00007eff740302a0 0000000000000001
Instructions: (pc=0x00007eff7b312b04)
0x00007eff7b312ae4: 8b 45 98 89 c1 41 f6 c0 01 4c 63 f9 4c 89 c0 0f
0x00007eff7b312af4: 85 6f ff ff ff 48 83 e0 fd 48 8d 15 f4 9a 64 00
0x00007eff7b312b04: 48 8b 00 48 c1 e8 03 83 e0 0f 3b 02 0f 82 68 ff
0x00007eff7b312b14: ff ff 4e 8d 1c fd 00 00 00 00 4d 8b 6c 24 30 4b
Register to memory mapping:
RAX=0x00000063f9000000 is an unknown value
RBX=0x00000007ba484dd8 is an oop
[C
- klass: {type array char}
- length: 5
RCX=0x0000000000000004 is an unknown value
RDX=0x00007eff7b95c5f8: <offset 0xfd85f8> in /opt/jdk1.8.0_261/jre/lib/amd64/server/libjvm.so at 0x00007eff7a984000
RSP=0x00007eff62bf9c60 is an unknown value
RBP=0x00007eff62bf9cf0 is an unknown value
RSI=0x00000007ba484dd8 is an oop
[C
- klass: {type array char}
- length: 5
RDI=0x00007eff74083240 is an unknown value
R8 =0x00000063f9000000 is an unknown value
R9 =0x0000000493436a5d is pointing into object: 0x0000000493436a50
java.lang.String
- klass: 'java/lang/String'
R10=0x00007eff7b928ea0: <offset 0xfa4ea0> in /opt/jdk1.8.0_261/jre/lib/amd64/server/libjvm.so at 0x00007eff7a984000
R11=0x0000000000000018 is an unknown value
R12=0x00007eff74083240 is an unknown value
R13=0x0000000000000001 is an unknown value
R14=0x00007eff740832a8 is an unknown value
R15=0x0000000000000004 is an unknown value
Stack: [0x00007eff62afb000,0x00007eff62bfb000], sp=0x00007eff62bf9c60, free space=1019k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [libjvm.so+0x98eb04] oopDesc* PSPromotionManager::copy_to_survivor_space<false>(oopDesc*)+0x204
V [libjvm.so+0x98e548] PSPromotionManager::drain_stacks_depth(bool)+0x788
V [libjvm.so+0x992b2e] StealTask::do_it(GCTaskManager*, unsigned int)+0x4ce
V [libjvm.so+0x5c6266] GCTaskThread::run()+0x1b6
V [libjvm.so+0x914d12] java_start(Thread*)+0x102