Java JNI 调用的开销

2022-09-04 21:43:32

可能的重复:
是什么使JNI调用速度变慢?

首先让我说,这个问题更多的是出于好奇心,而不是真正的必要性。

我很想知道从Java执行JNI调用的开销是多少,比如说,与分配数组并使用for循环复制元素相比。System.arraycopy

如果开销很大,那么可能有一个粗略的元素“幻数”,它只需使用for循环而不是使用System调用即可补偿这些元素。此外,导致此开销的系统调用中究竟涉及什么?我猜堆栈必须推送到调用的上下文中,这可能需要一段时间,但我找不到整个过程的良好解释。

让我澄清我的问题:

我知道使用数组复制是在Java中复制数组的最快方法。

话虽如此,假设我正在使用它来复制只有一个元素的数组。由于我调用底层操作系统来执行此操作,因此此调用中必须存在开销。我很想知道这个开销是什么,以及调用过程中发生了什么。

我很抱歉,如果使用数组复制误导你从我的问题的目的。我很想知道 JNI 调用的开销,以及实际调用中涉及的内容。


答案 1

由于我调用底层操作系统来执行此操作...

你是对的,系统调用相当昂贵。但是,in有点用词不当。不涉及任何系统调用。SystemSystem.arraycopy()

...此调用中必须有开销。我很想知道这个开销是什么,以及调用过程中发生了什么。

当您查看 的定义时,它被声明为 。这意味着该方法是在C++中实现的。如果您愿意,可以查看JDK源代码,并找到C++函数。在 OpenJDK 7 中,它被称为 并位于 .实现出奇地复杂,但在内心深处,它本质上是一个memcpy()。System.arraycopy()nativeJVM_ArrayCopy()hotspot/src/share/vm/prims/jvm.cpp

如果 用作正常的本机函数,则调用它会产生开销。参数检查等会导致进一步的开销。arraycopy()

但是,JIT 编译器很可能知道 .这意味着,编译器知道如何生成特制的机器代码来执行数组复制,而不是调用C++函数。我不知道其他JVM,但HotSpot确实对有这种“内在”支持。System.arraycopy()System.arraycopy()

假设我用它来复制一个只有一个元素的数组

如果你的数组很小,你可以用手工制作的循环来击败它。如果在编译时知道大小,则可以做得更好,因为这样您也可以展开循环。但是,除了在最狭窄的情况下,所有这些都不是真正相关的。System.arraycopy()


答案 2

看看java.util.Arrays.copyOf实现,例如

public static byte[] copyOf(byte[] original, int newLength) {
    byte[] copy = new byte[newLength];
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

他们使用System.arraycopy,因为这是最快的方法。

如果你的意思是在Java中调用本机方法是否昂贵,那么看看 http://www.javamex.com/tutorials/jni/overhead.shtml

更新问题真的很有趣,所以我做了一些测试

        long t0 = System.currentTimeMillis();
        byte[] a = new byte[100];
        byte[] b = new byte[100];
        for(int i = 0; i < 10000000; i++) {
//            for(int j = 0; j < a.length; j++) {
//                a[j] = b[j];
//            }
            System.arraycopy(b, 0, a, 0, a.length);
        }
        System.out.println(System.currentTimeMillis() - t0);

它表明,在非常短的数组(<10)上,System.arraycopy可能更慢,很可能是因为它是本机的,但在更大的数组上,它不再重要,System.arraycopy要快得多。