问题
在尝试使用JNI接口时,我想知道是否可以将JObject
转换为等效的结构体以操作其中的字段。但是,当我尝试时,我很惊讶地发现这并不起作用。忽略这个想法有多糟糕,为什么它不起作用?
我的方法
Java测试类
我创建了一个简单的类Point
来进行测试。Point
有两个字段和一个构造函数,该函数接受x和y以及一些根据字段返回信息的随机方法。
public class Point {
public final double x;
public final double y;
// As well as some random methods
}
内存布局
使用 jol 工具,我查看了 Java 运行时中 Point
类的结构(如下所示)。
C:\Users\home\IdeaProjects\test-project>java -cp jol-cli-0.9-full.jar;out\production\java-test org.openjdk.jol.Main internals Point
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Instantiated the sample instance via public Point(double,double)
Point object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 31 32 01 f8 (00110001 00110010 00000001 11111000) (-134139343)
12 4 (alignment/padding gap)
16 8 double Point.x 0.0
24 8 double Point.y 0.0
Instance size: 32 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
测试结构体
我写了一个简单的测试结构体,与jol所描述的内存模型相匹配,并进行了一些测试,以确保它具有相同的对齐方式和每个元素具有正确的偏移量。我使用Rust编写了这个测试结构体,但对于任何其他编译语言来说,应该是相同的。
#[derive(Debug)]
#[repr(C, align(8))]
pub struct Point {
header1: u32,
header2: u32,
header3: u32,
point_x: f64,
point_y: f64,
}
输出
我的测试的最后一部分是创建一个jni函数,该函数接受一个Point
对象,并将该点对象转换为点结构体。
C头文件(作为参考)
/*
* Class: Main
* Method: analyze
* Signature: (LPoint;)V
*/
JNIEXPORT void JNICALL Java_Main_analyze
(JNIEnv *, jclass, jobject);
Rust实现
#[no_mangle]
pub extern "system" fn Java_Main_analyze(env: JNIEnv, class: JClass, obj: JObject) {
unsafe {
// De-reference the `JObject` to get the object pointer, then transmute the
// pointer into my `Point` struct.
let obj_ptr = mem::transmute::<_, *const Point>(*obj);
// Output the debug format of the `Point` struct
println!("{:?}", *obj_ptr);
}
}
运行
每次运行时,我都会得到不同的结果。
// First Run:
Point { header1: 1802087032, header2: 7, header3: 43906792, point_x:
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000230641669, point_y:
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000021692881 }
// Second Run:
Point { header1: 1802087832, header2: 7, header3: 42529864, point_x:
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000229832192, point_y:
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000021012588 }
版本信息
C:\Users\home\IdeaProjects\test-project>java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
编辑:我是在 Windows 10 Home 10.0.18362 Build 18362
上完成的。
编辑2:由于我使用了Rust来解决这个问题,所以我使用了Rust的jni
crate。它提供了我上面提到的JObject
类型。我刚才想到可能会有一些混淆,因为JObject
与C头文件中显示的jobject
不同。JObject
是一个指向jobject
的指针的Rust包装器,因此我在转换指针之前对其进行了取消引用。
struct Point { uint32_t hdr[3]; double x, y; };
和Point* point = *reinterpret_cast<Point**>(obj);
(macOS 10.14.6,openjdk 1.8.0_66-b17 64 位)。 - Botjejobject
所指向的对象内部是什么。因此,使用mem::transmute()
只是一种无限乐观的做法。 - user207421jobject->oop->oopDesc->{类指针、GC信息、对象字段}
。这意味着jobject
在任何有用的意义上都不是Java对象。 - user207421