Проблема
Когда я экспериментировал с интерфейсом 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Я обнаружил структуру моего Point
класса во время выполнения Java (показано ниже).
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: Поскольку для решения этой проблемы я использовал ржавчину, я использовал ржавчину jni
ящик . Это обеспечило JObject
тип, который я упомянул выше. Мне только что пришло в голову, что может быть некоторая путаница, поскольку JObject
не совпадает с jobject
, показанным в заголовке C. JObject
- это оболочка от ржавчины вокруг указателя на jobject
, поэтому я разыменую его перед преобразованием указателя.