2014-09-12 46 views
1

我正在设计一个JNI接口,将字符串参数从Java传递给C++。我需要高性能,并且能够使用Direct ByteBuffer和String.getBytes()很好地完成这项工作,但将字符串传递给C/C++的代价仍然相当高。我最近阅读了关于Open JDK的Unsafe类。 This excellent page让我开始了,但我发现不安全是可悲的,但可以理解的是记录不完善。固定不安全的指针

我想知道,如果我使用不安全类获取指向字符串的指针并将其传递给C++,那么在输入C++代码之前是否存在该对象已移动的风险?即使在C++正在执行?还是由不安全代码提供的这些地址固定?如果他们不固定,这些不安全指针如何有用?

+0

如果您发布了一些代码,它将有很大的帮助。特别是,它将有助于了解C++代码是否从Java位同步调用,或者是否有多个线程。另外,通过指向字符串的指针是指'char *'还是'std :: string *'?用代码回答很多! – Luis 2014-09-12 19:43:29

+0

看来惩罚是从UTF-16转换为你的C++代码使用的任何编码。如果您的C++代码可以处理已计数(未终止)的UTF-16,则不需要使用'GetStringChars'和'GetStringLength'进行转换或复制。这不是你所说的“指向一个字符串的指针?” – 2014-09-13 21:44:03

+0

汤姆。即使你对它没有做任何事情,从Java-> JNI-> C++传递一个对象(包括字符串)似乎会有性能损失。这个成本明显高于通过很长时间的成本。我不确定这个成本是否仅仅是因为创造了一个隐含的本地参考。不管什么原因,这个代价是痛苦的。另外GetStringChars也有一个可以衡量的成本。我想我读过那是因为有人收到一个指向字节COPY的指针......或者该对象必须被固定。无论哪种方式,你可以想象,造成延迟。 – user3624334 2014-09-16 18:40:51

回答

1

不安全并不意味着与JNI互操作。所以通过不安全获得可能会改变任何时间(即使与您的C++并行)。

JNI API能够将内存中的对象固定到内存中以访问阵列内容(在HotSpot JVM中,它会阻止GC,因此可能会对GC暂停持续时间产生负面影响)。

特别是,Get * ArrayElements会固定数组,直到您显式执行Release * ArrayElements。 GetStringChars的工作方式类似。

直接字节缓冲区保存指向堆外的内存缓冲区的指针,但是此缓冲区不移动,您可以访问它以获得本机代码。

1

我读了java.misc.Unsafe的Java source,并有更多的见解。

不安全有至少两种处理内存的方式。

  1. allocateMemory/reallocateMemory/freeMemory /等 - 据我可以告诉这个分配的内存外堆所以没有面临GC'ing挑战。我已经间接测试了这一点,看起来很长的返回只是一个指向内存的指针。这种类型的内存很可能很安全地通过JNI传递给本地代码。应用程序Java代码应该能够在JNI调用之前和之后快速修改/查询它,方法是使用支持此类型内存指针的其他一些内置Unsafe方法。

  2. object + offset - 这些方法接受一个指向对象的指针和一个“偏移”标记,以指示对象在何处获取/修改该值。这些对象大概总是在Java堆中,但将对象传递给这些方法可能有助于解决GC并发症。它听起来像“偏移量”有时是一个“饼干”,而不是一个实际的偏移量,但它也听起来像在数组的情况下,arrayBaseOffset()返回一个“偏移量”,可以算术操纵。我不知道这个对象+偏移对于JNI代码是否安全。我没有看到一种方法可以直接生成一个指向堆中的Java对象的指针,而该对象可以(危险地)通过JNI。可以传递一个对象和偏移量,但是考虑到通过JNI传递对象的成本,这种方法无论如何都不具吸引力。

赞(1),与我在帖子中引用的页面相关联的code可能是用于JNI的相互作用非常安全的。它在处理String时采用对象+偏移方法,但在处理直接ByteBuffer时使用方法(1),它总是位于Java堆外部。 Direct ByteBuffer的JNI非常友好,并且通常可以避免JNI Object传递成本的方式,这些成本是我在上面对Tom的评论中提到的。