13

可能重复:
是什么让 JNI 调用变慢?

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

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

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

让我澄清一下我的问题:

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

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

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

4

4 回答 4

9

因为我正在调用底层操作系统来这样做......

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

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

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

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

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

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

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

于 2012-12-18T10:29:28.823 回答
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

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

        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 更快。

于 2012-12-18T10:26:09.237 回答
1

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

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

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


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

于 2012-12-18T10:51:33.070 回答
0

你搞错了。System.arraycopy() 是 JVM 提供的超快速原生实现

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

于 2012-12-18T10:23:30.407 回答