Java JNI调用的开销

时间:2012-12-18 10:20:16

标签: java arrays jvm

  

可能重复:
  What makes JNI calls slow?

首先让我说,这些问题的出现更多是出于好奇而不是真正的必然。

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

如果开销很大,那么可能有一个粗略的“神奇数字”的元素,它补偿简单地使用for循环,而不是使用系统调用。而且,系统调用中究竟涉及到什么导致这种开销? 我猜测必须将堆栈推送到调用的上下文,这可能需要一段时间,但我无法找到整个过程的良好解释。

让我澄清一下我的问题:

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

话虽如此,让我说我用它来复制一个只有一个元素的数组。由于我正在调用底层操作系统,因此作为此调用的开销。我很想知道这个开销是什么以及在调用过程中会发生什么。

如果使用arraycopy误解了我的问题,我很抱歉。我很想知道JNI呼叫的开销,以及实际呼叫中涉及的内容。

4 个答案:

答案 0 :(得分:9)

  

因为我正在调用底层操作系统...

你是对的,system calls相当昂贵。但是,System中的System.arraycopy()有点用词不当。没有系统调用。

  

......此次通话必须有开销。我很想知道这个开销是什么以及在调用过程中会发生什么。

当您查看System.arraycopy()的定义时,它被声明为native。这意味着该方法是用C ++实现的。如果您如此倾向,可以查看JDK源代码,并找到C ++函数。在OpenJDK 7中,它被称为JVM_ArrayCopy()并且位于hotspot/src/share/vm/prims/jvm.cpp。实现非常复杂,但在内心深处它实际上是memcpy()

如果将arraycopy()用作普通本机函数,则调用它会产生开销。由参数检查等引起的进一步开销。

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

  

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

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

答案 1 :(得分: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);

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

答案 2 :(得分:1)

  

我很想知道JNI调用的开销,以及实际调用中涉及的内容。

System.arraycopy()方法相当复杂 * ,JIT编译器不太可能内联它(正如其他答案所暗示的那样)。

另一方面,JIT编译器可能使用优化的调用序列,因为这是一个固有的本机方法。换句话说,这很可能不是正常的JNI呼叫。


* - System.arraycopy不是简单的内存副本。它必须测试其参数以避免读取或写入超出数组边界,等等。在从一个对象数组复制到另一个对象数组的情况下,可能需要检查复制的每个对象的实际类型。所有这些加起来的代码远远多于内联的合理代码。

答案 3 :(得分:0)

你有错误的方法。 System.arraycopy()是JVM提供的超快速本机实现

没有“开销” - 只有“优势”