固定不安全的指针

Pinning Unsafe pointer

本文关键字:指针 不安全      更新时间:2023-10-16

我正在设计一个JNI接口,它将字符串参数从Java传递到C++。我需要高性能,并且已经能够使用Direct ByteBuffer和String.getBytes()很好地做到这一点,但将字符串传递到C/C++的代价仍然很高。我最近读到了OpenJDK的Unsafe类。这个优秀的页面让我开始了,但我发现《不安全》很糟糕,但可以理解的是,它的文档记录得很差。

我想知道,如果我使用Unsafe类获得一个指向字符串的指针并将其传递给C++,是否存在在输入C++代码之前对象已经移动的风险?即使在C++执行的时候?或者这些地址是由不安全代码提供的?如果它们没有被固定,那么这些不安全的指针怎么会有用呢?

Unsafe并不意味着与JNI进行互操作。因此,通过Unsafe获得的内容可能随时更改(即使与C++并行)。

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

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

直接ByteBuffer持有指向堆外内存缓冲区的指针,前提是该缓冲区没有移动,您可以访问它以获取本机代码。

我已经阅读了Java.misc.Unsafe的Java源代码,并有了更多的见解。

Unsafe至少有两种处理内存的方法。

  1. allocateMemory/reallocateMemoory/freeMemory/etc/strong>——据我所知,这种内存分配是在堆外的,因此不会面临GC’ing挑战。我已经间接地测试了这一点,似乎长返回只是指向内存的指针。这种类型的内存很可能可以安全地通过JNI传递给本机代码。应用程序Java代码应该能够通过使用其他一些支持这种内存指针风格的内在不安全方法,在JNI调用前后快速修改/查询它。

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

像(1)一样,与我在帖子中引用的页面相关的代码对于JNI交互来说可能是非常安全的。它在处理String时采用对象+偏移量的方法,但在处理直接ByteBuffer时使用方法(1),后者总是位于Java堆之外。Direct ByteBuffer对JNI非常友好,通常可以避免我在上面对Tom的评论中提到的JNI对象传递成本。