当使用JNI桥接c ++和Java时,我们总是希望避免不必要的复制。我发现GetPrimitiveArrayCritical
可能会给我们很高的机会不复制数组。但我并不完全理解其记录的限制here:
调用GetPrimitiveArrayCritical之后,本机代码在调用ReleasePrimitiveArrayCritical之前不应该运行一段时间。我们必须将这对函数中的代码视为在关键区域中运行。"在关键区域内,本机代码不能调用其他JNI函数,也不能调用可导致当前线程阻塞并等待另一个Java线程的任何系统调用 。 (例如,当前线程不得对另一个Java线程正在写入的流调用read。)
这些限制使本机代码更有可能获得阵列的未复制版本,即使VM不支持固定。
我的问题是:
延长时间的确切含义是什么?
这是否意味着我们可以安全地调用其他JNI函数或系统调用,它们永远不会导致当前线程阻塞并等待另一个Java线程?
GetPrimitiveArrayCritical是否是线程安全的?
使用GetPrimitiveArrayCritical而不是GetArrayRegion时,我应该知道什么?
答案 0 :(得分:18)
这里要理解的关键是你要在这块记忆中获得一个关键部分(例如锁)。
延长的时间段旨在表明,一旦您持有此锁,您就会阻止JVM执行常规操作。所以你应该尽快做你需要做的任何处理。你当然不想做一些可能阻止的操作,例如,当你将系统完全停止时。
你可能能够逃脱它,因为我怀疑这个锁做的主要事情是防止垃圾收集,但文档很清楚它不支持调用其他JNI函数的行为。因此,您可能会发现您的代码在JVM的一个版本中运行,而在其他版本中运行。
因为这是获取锁定(关键部分),是的,它是线程安全的。因为它是锁定的,所以你不应该持有它太长时间(见1)。
GetArrayRegion将始终为您提供副本,GetPrimitiveArrayCritical 可能为您提供副本,或可能为您提供直接指针。它不确定的原因是它为JVM实现者提供了更多的未来灵活性,以避免直接指针,如果它们会过多地影响一般VM性能(即可能对某些垃圾收集器造成太大影响以使其值得允许锁定)
答案 1 :(得分:8)
GetPrimitiveArrayCritical
将阻止所有现有的垃圾收集器。 (实验性的Shenandoah收集器通常不会阻塞。)阻塞垃圾收集器将阻止所有对象分配(一旦垃圾堆积起来)。
因此,使用GetPrimitiveArrayCritical
的规则如下:
GetPrimitiveArrayCritical
的性能优势将被完全否定。然而,从正确的角度来看,失速是安全的(与死锁相反)。Get*Critical
和Release*Critical
方法,它们是线程安全的。null
的返回值并正确设置mode
,因为允许Get*Critical
方法失败和/或制作副本,就像Get*ArrayElements
一样。GetPrimitiveArrayCritical
,请创建一个运行时选项以使用Get*ArrayElements
。即使您没有遇到由此产生的拖延GetPrimitiveArrayCritical,您的用户可能。如果在关键区域内调用JNI函数,Java标志-Xcheck:jni
将发出警告。忽略文档说明有时可以在关键区域内调用JNI函数。它不是。
Java 8标志-XX:+PrintJNIGCStalls -XX:+PrintGCDetails
将打印有关停滞分配和集合的有用日志消息。要查找的消息可以从src/share/vm/memory/gcLocker.cpp
在Java 9中,日志记录已更改。打开gc和jni的日志记录。要查找的消息可以从src/share/vm/gc/shared/gcLocker.cpp
收集更多信息:
答案 2 :(得分:1)
GetByteArrayElements方法无法保证您的程序使用引用或复制。 JNI返回isCopy标志,表示它复制了对象或固定它(pin表示引用)。如果你不想永远复制它,你不必使用GetArrayElements方法,因为它总是返回副本(JVM决定是否复制,可能是复制首选,因为复制减轻了垃圾收集器的负担)。我试过了,看到我的ram在发送一个大阵列时增加了。您还可以在以下链接中看到:
IBM copy and pin(从树视图看复制和图钉主题)
正如文档所述,GetPrimitiveArrayCritical返回Java数组的直接堆地址,禁用垃圾收集,直到调用相应的ReleasePrimitiveArrayCritical。所以你必须使用GetPrimitiveArrayCritical,如果你不想复制(当你拥有一个大数组时需要它。)
要了解GetArrayRegion,您可以阅读以下链接:
我想如果你想获得所有数组,请使用GetPrimitiveArrayCritical,如果你想获得一个数组,请使用GetArrayRegion。