我正在设计一个将字符串参数从Java传递到C ++的JNI接口。我需要高性能并且能够使用Direct ByteBuffer和String.getBytes()来做得相当好,但是将字符串传递给C / C ++的代价仍然很高。我最近读到了Open JDK的不安全类。 This excellent page让我开始,但我发现不安全是悲惨的,但可以理解的是记录不清。
我想知道,如果我使用Unsafe类获取指向字符串的指针并将其传递给C ++,那么在输入C ++代码之前是否存在对象移动的风险?甚至在C ++执行时?或者这些地址由不安全代码以某种方式固定?如果它们没有固定,这些不安全指针有用吗?
答案 0 :(得分:1)
不安全并不意味着与JNI互动。所以通过Unsafe获得可以随时改变(甚至与你的C ++并行)。
JNI API能够在内存中固定对象以访问数组内容(在HotSpot JVM中它会阻止GC,因此可能对GC暂停持续时间产生负面影响)。
特别是,在您明确执行Release * ArrayElements之前,Get * ArrayElements会固定数组。 GetStringChars以类似的方式工作。
Direct ByteBuffer保持指向堆外部的内存缓冲区的指针,此缓冲区没有移动,您可以访问本机代码。
答案 1 :(得分:1)
我已经为java.misc.Unsafe阅读了Java source,并且有了更多的见解。
Unsafe至少有两种处理内存的方法。
allocateMemory / reallocateMemory / freeMemory / etc - 据我所知,这种内存分配在堆外,因此不会面临GC的挑战。我已间接测试了这一点,似乎返回的long只是一个指向内存的指针。这种类型的内存似乎很可能安全地通过JNI传递给本机代码。应用程序Java代码应该能够通过使用支持这种内存指针的一些其他内在的Unsafe方法,在JNI调用之前和之后快速修改/查询它。
object + offset - 这些方法接受指向对象的指针和“offset”标记,以指示对象中取值/修改值的位置。对象可能总是在Java堆中,但将对象传递给这些方法可能有助于解决GC并发症。它听起来像“偏移”有时是一个“cookie”而不是一个实际的偏移,但它也听起来像在数组的情况下,arrayBaseOffset()返回一个可以算术操作的“偏移”。我不知道这个对象+偏移是否对JNI代码是安全的。我没有看到一种方法来直接生成指向堆中的Java对象的指针(危险地)可以通过JNI。可以传递一个对象和偏移量,但考虑到通过JNI传递对象的成本,这种方法无论如何都没有吸引力。
与(1)类似,与我在帖子中引用的页面相关联的code可能对JNI交互非常安全。它在处理String时采用对象+偏移方法,但在处理直接ByteBuffer时使用方法(1),它直接位于Java堆之外。 Direct ByteBuffer对JNI非常友好,并且通常可以在避免JNI对象传递成本的方式使用它,我在上面对Tom的评论中提到了这一点。