【问题标题】:Overhead of a Java JNI call [duplicate]Java JNI 调用的开销 [重复]
【发布时间】:2012-12-18 10:20:16
【问题描述】:

可能重复:
What makes JNI calls slow?

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

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

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

让我澄清一下我的问题:

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

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

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

【问题讨论】:

标签: java arrays jvm


【解决方案1】:

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

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

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

当您查看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 关于内联原生调用这一点是最重要的贡献。
  • 您的回答,以及指向我的评论:stackoverflow.com/questions/7699020/what-makes-jni-calls-slow 是最有帮助的,所以我接受。感谢您清理了一些我没有说清楚的术语。
  • +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

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);

它表明在非常短的数组(

【讨论】:

  • 谢谢,为链接点赞,这确实是我想要的。
  • 我添加了一些测试,看我的更新
  • "... 很可能是因为它是原生的" - 还有其他可能的解释;例如因为arraycopy() 的簿记必须检查参数,并选择正确的复制方式。
  • 对,“...因为它是原生的”似乎不是一个很好的解释,我实际上是指人们所说的所有关于 jni 开销的事情...
【解决方案3】:

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

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

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


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

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 2012-12-08
      • 1970-01-01
      • 2016-05-18
      • 1970-01-01
      • 1970-01-01
      • 2014-09-12
      • 1970-01-01
      • 1970-01-01
      • 2010-12-19
      相关资源
      最近更新 更多