什么';这是使用GetPrimitiveArrayCritical和Get<;基本类型>;ArrayRegion? 使用JNI桥接C++和java时,我们总是希望避免不必要的复制。我发现GetPrimitiveArrayCritical可能会给我们提供不复制数组的高机会。但我不完全理解它的限制:

什么';这是使用GetPrimitiveArrayCritical和Get<;基本类型>;ArrayRegion? 使用JNI桥接C++和java时,我们总是希望避免不必要的复制。我发现GetPrimitiveArrayCritical可能会给我们提供不复制数组的高机会。但我不完全理解它的限制:,java,c++,arrays,multithreading,java-native-interface,Java,C++,Arrays,Multithreading,Java Native Interface,调用GetPrimitiveArrayCritical后,本机代码在调用ReleasePrimitiveArrayCritical之前不应运行较长时间。我们必须将这对函数中的代码视为在“关键区域”中运行。在关键区域内,本机代码不得调用其他JNI函数,或任何可能导致当前线程阻塞并等待另一个Java线程的系统调用。(例如,当前线程不能对另一个Java线程正在写入的流调用read。) 这些限制使得本机代码更有可能获得阵列的未复制版本,即使VM不支持固定 我的问题是: 延长时间的确切含义是什么 那么,

调用GetPrimitiveArrayCritical后,本机代码在调用ReleasePrimitiveArrayCritical之前不应运行较长时间。我们必须将这对函数中的代码视为在“关键区域”中运行。在关键区域内,本机代码不得调用其他JNI函数,或任何可能导致当前线程阻塞并等待另一个Java线程的系统调用。(例如,当前线程不能对另一个Java线程正在写入的流调用read。)

这些限制使得本机代码更有可能获得阵列的未复制版本,即使VM不支持固定

我的问题是:

  • 延长时间的确切含义是什么

  • 那么,这是否意味着我们可以安全地调用其他JNI函数或系统调用,从而永远不会导致当前线程阻塞并等待另一个Java线程

  • GetPrimitiveArrayCritical线程安全吗

  • 使用GetPrimitiveArrayCritical而不是GetArrayRegion时,有什么我应该知道的吗


  • 这里要理解的关键是,您正在获取这段内存的关键部分(例如锁)

  • 延长的时间段旨在表明,一旦您持有此锁,您将阻止JVM执行通常的操作。因此,您应该尽可能快地完成需要执行的任何处理。例如,您当然不希望执行可能会阻塞的do操作,因为这样会使系统完全停止

  • 您可能可以侥幸逃脱,因为我怀疑这个锁的主要作用是防止垃圾收集,但是文档很清楚,调用其他JNI函数不受支持。因此,您可能会发现您的代码在一个JVM版本中工作,而不是在其他版本中工作

  • 因为这是获取锁(关键部分),是的,它是线程安全的。因为它是一把锁,你不应该握得太久(见1)

  • GetArrayRegion将始终为您提供副本,GetPrimitiveArrayCritical可能会为您提供副本或直接指针。不确定的原因是,如果直接指针对一般VM性能的影响太大(即,可能对某些垃圾收集器的影响太大,因此允许锁定是值得的),它将为JVM实现者提供更大的灵活性,以避免直接指针


  • GetByteArrayElements方法不能保证程序使用引用或复制。JNI返回isCopy标志,表示它复制了对象或固定了对象(固定表示引用)。如果您不想永远复制它,就不必使用GetArrayElements方法,因为它总是返回copy(JVM决定是否复制,并且可能优先选择复制,因为复制减轻了垃圾收集器的负担)。我试了一下,发现当发送一个大数组时,我的内存增加了。您还可以在下面的链接中看到:

    (查看treeview中的副本和pin主题)

    如文档所述,GetPrimitiveArrayCritical返回Java数组的直接堆地址,在调用相应的ReleasePrimitiveArrayCritical之前禁用垃圾收集。所以,如果不想复制,就必须使用GetPrimitiveArrayCritical(当你有一个大数组时,你需要它)

    要了解GetArrayRegion,您可以阅读以下链接:


    我假设,如果您想要获取整个数组,请使用GetPrimitiveArrayCritical;如果您想要获取一个数组,请使用GetArrayRegion。

    GetPrimitiveArrayCritical
    将阻止所有现有的垃圾收集器。(实验中的Shenandoah收集器通常不会阻塞。)阻塞垃圾收集器将阻塞所有对象分配(一旦垃圾堆积起来)

    因此,使用
    GetPrimitiveArrayCritical
    的规则如下:

  • 不要调用任何JNI函数。各部分的文档没有充分强调这一点,但这是您必须遵守的规则。原因是JNI函数可能会分配内存,特别是本地引用。因为没有记录哪些JNI函数分配内存,或者分配了多少内存,所以您不能调用其中任何一个。据推测,函数EnsureLocalCapacity可以预先分配本地引用来解决此问题,但没有人记录如何使用它。不要在关键区域内调用除GetPrimitiveArrayCritical、GetStringCritical、ReleasePrimitiveArrayCritical和ReleaseStringCritical之外的JNI函数,否则会死锁
  • 不要以任何其他方式阻止可能需要从堆中分配内存的代码。这主要禁止阻止在同一VM中运行的Java代码。可以想象(但我不能肯定),您可以阻止不分配资源的Java代码
  • 您可以从其他线程调用JNI函数,只要您不阻止等待这些线程。调用JNI函数的线程可能会暂停。见下一点
  • 在关键区域花费太多时间将导致其他线程暂停。根据您正在运行的线程数量及其分配率,您在关键区域花费的时间可能会有所不同。在单线程应用程序中,或者在执行很少分配的多线程应用程序中,您可以在关键区域安全地花费无限时间。但是,在其他情况下,您将暂停线程太多,以至于
    GetPrimitiveArrayCritical
    的性能优势将被完全否定。然而,拖延是不可能的