我很难理解sun.misc.Unsafe的文档 —— 我猜这是因为它不是为了普通使用而设计的,所以没有人真正关心它是否易读。但我实际上确实需要一种方法来查找数组中元素的地址(这样我就可以传递一个指向该地址的指针给本机代码)。是否有人有任何有效的代码可以做到这一点?它是可靠的吗?
这是一个可用的示例,请注意,如果不合适地使用Unsafe
类,可能会导致JVM崩溃。
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class UnsafeTest {
public static void main(String... args) {
Unsafe unsafe = null;
try {
Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (sun.misc.Unsafe) field.get(null);
} catch (Exception e) {
throw new AssertionError(e);
}
int ten = 10;
byte size = 1;
long mem = unsafe.allocateMemory(size);
unsafe.putAddress(mem, ten);
long readValue = unsafe.getAddress(mem);
System.out.println("Val: " + readValue);
}
}
char* mem = malloc(size);...
,它不会被GC所触及,并且会导致本地C泄漏,除非释放它。 - bestsss如果不想使用数组,可以使用ByteBuffer.allocateDirect()直接缓冲区。该缓冲区在一个字段中具有地址,并且此地址在ByteBuffer的生命周期内不会改变。直接ByteBuffer使用最小的堆空间。您可以使用反射来获取地址。
您可以使用Unsafe来获取地址,但问题是GC随时可能将其移动。对象在内存中不是固定的。
在JNI中,您可以使用特殊方法将数据复制到/从Java对象中以避免此问题(以及其他问题)。如果要在C代码和对象之间交换数据,建议使用这些方法。
GetPrimitiveArrayCritical
,但这可能会影响GC。勇敢地将其复制到直接(缓冲区)内存中。这是唯一可行的解决方案。例如,实现FileOutputStream(SocketOutputStream扩展它)使用堆栈上元素的副本。复制的惩罚并不高,因为最高成本是数据的加载成本(甚至是缓存未命中),无论如何都必须支付。复制还会导致预取缓存行,因此根据本机代码的工作方式,情况可能会更好。 - bestsss为什么?JNI中有很多处理Java数组内容的工具,您不需要使用未经记录的内部Sun类,因为它们可能在下周不存在。
Unsafe
返回的指针时移动对象吗? - Stephen CallocateMemory
这样的方法实际上是在堆之外分配内存。这就是为什么它们不会移动的原因。但是@Jules所要求的是一种获取堆中对象地址的方法。那是不同的。我相信你可以使用Unsafe来做到这一点...但这是不安全的! - Stephen C