JNI对象指针 [英] JNI Object Pointers

查看:90
本文介绍了JNI对象指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当尝试使用JNI接口时,我想知道是否可以将JObject转换为等效的结构以操纵字段.但是,当我尝试时,我惊讶地发现这没有用. 忽略了这个想法可能多么可怕,为什么它不起作用?

When experimenting with the JNI interface, I was wondering if I could take a JObject and transmute it into an equivalent struct to manipulate the fields. However, when I tried I was surprised to see that this did not work. Ignoring how horrible this idea might be, why didn't it work?


我做了一个简单的类Point来进行测试. Point有两个字段和一个构造函数,它接受x和y以及一些随机的方法,这些方法根据这些字段返回信息.

I made a simple class Point to do my test. Point has two fields and a constructor that takes in an x and y as well as a few random methods that return information based on the fields.

public class Point {
    public final double x;
    public final double y;
    // As well as some random methods
}

内存布局

使用jol,我在Java运行时中找到了Point类的结构(如下所示).

Memory Layout

Using jol, I found the structure of my Point class in the java runtime (shown below).

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进行了此操作,但对于任何其他编译语言都应该相同.

Test Struct

I wrote out a simple test struct that matched the memory model described by jol along with some tests to make sure that it had the same alignment and each element had the correct offset. I did this using rust, but it should be the same for any other compiled language.

#[derive(Debug)]
#[repr(C, align(8))]
pub struct Point {
    header1: u32,
    header2: u32,
    header3: u32,
    point_x: f64,
    point_y: f64,
}

输出

我的测试的最后一部分是制作一个jni函数,该函数接受一个Point对象并将该点对象转换为点结构.

Output

The final part of my test was making a jni function that took in a Point object and transmuted the point object into the point struct.

/*
 * Class:     Main
 * Method:    analyze
 * Signature: (LPoint;)V
 */
JNIEXPORT void JNICALL Java_Main_analyze
  (JNIEnv *, jclass, jobject);

严格执行

#[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);
    }
}

运行

每次运行它,我都会得到不同的结果.

Runs

Every time I ran it, I got a different result.

// 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

由于我使用了锈来解决此问题,因此我使用了锈的 板条箱.它提供了我上面提到的JObject类型.我只是想到可能会有些困惑,因为JObject与C标头中显示的jobject不同. JObject是指向jobject的指针的防锈包装,因此在转换指针之前,我先取消引用它.

Edit 2: Since I used rust to approach this problem I used rust's jni crate. It provided the JObject type I mentioned above. It just occurred to me that there might be some confusion since JObject is not the same as the jobject shown in the C header. JObject is a rust wrapper around a pointer to a jobject, hence my dereferencing it before transmuting the pointer.

推荐答案

概念性解释

阅读论文关于内存压缩,我了解到Java引用是由两个指针组成的. 在垃圾收集期间,将执行压缩步骤.为了确保引用仍然对齐,使用了两个指针来防止对象移动时的损坏.引用由指向另一个指向该对象的指针的指针组成.当执行压缩步骤并将对象移动到内存中时,只需更改第二个指针即可.

Conceptual Explanation

After reading a paper on memory compaction, I learned that java references are made of two pointers. During garbage collection, a compaction step occurs. To ensure that the references will still line up, two pointers are used to prevent mangling when objects are moved. A reference consists of a pointer to another pointer that points to the object. When the compaction step occurs and an object is moved through memory, only the second pointer needs to be altered.

换句话说,引用实际上是指向内存中指向对象的位置的指针.

In other words a reference is really a pointer to the location in memory that points to an object.

我知道我弄乱了这个解释,但希望它大部分都是可读/准确的.

#[no_mangle]
pub extern "system" fn Java_Main_analyze(env: JNIEnv, class: JClass, obj: JObject) {
    unsafe {
        // It should have been transmuted to a pointer to a pointer and dereferenced twice.
        let indirect = mem::transmute::<_, *const *const Point>(*obj);
        println!("{:?}", **indirect);
    }
}

新输出

一旦应用了此修复程序,对象数据就会开始正确对齐. x和y与测试对象中的相同,并且所有三个标头均与jol对内存格式的预测一致(我假设如果使用带符号整数,标头3将会相同).

New Output

Once that fix was applied, the object data began to line up correctly. The x and y are the same as in the test object, and all three headers line up with jol's prediction for the memory format (I assume header 3 would have been the same if I had used a signed integer).

Point { header1: 1, header2: 0, header3: 4160799044, point_x: -3.472, point_y: 4.0 }

响应@Botje :我的Point结构正确,但是您无法重新创建该错误,因为从一开始就正确地解决了问题,而我却没有.

In response to @Botje: My Point struct was correct, but you were unable to recreate the error because you approached the problem correctly from the start while I did not.

这篇关于JNI对象指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆