JNA 数组和指针

发布于 2024-12-07 09:04:03 字数 1066 浏览 0 评论 0原文

我目前正在开发一个项目,需要我从 C 库接收 Java 调用。基本上我调用一个带有函数指针的 C 函数,然后 C 函数使用函数指针作为回调。我使用 JNA 传递一个 Java 对象(从现在起我将其称为 Callback 对象)作为回调函数。回调对象有一个接收 C 结构(称为 Frame)的方法,该结构包含 16 个元素的字节数组以及其他变量。我已经用标准 JNA 方式将此结构包装在 Java 类中,如下所示:

class Frame extends Structure {
    public short port;
    public short flags;
    public Pointer name // this is a pointer to the byte array
    public int rateDivisor;
}

回调机制工作正常!回调对象从 C 接收一个 Frame 对象,但是当我尝试使用 name.getByteArray(0, 16) 从指针获取字节数组时,应用程序因访问冲突异常而崩溃。但是,如果我用字节数组替换指针:

class Frame extends Structure {
    public short port;
    public short flags;
    public byte[] name = new byte[16];
    public int rateDivisor;
}

那么代码就可以正常工作!然而,我不想使用这段代码是有充分理由的。每次我调用该函数时,返回的 Frame 实际上是同一个对象(它只是被重用),但字节数组是一个新对象。这个回调函数每秒被调用很多次,这会导致垃圾收集器疯狂地吞噬数千个临时数组。该项目对性能至关重要,因此我真的不希望在应用程序的这一部分中出现任何临时对象。

我的猜测是,通过在 Frame 类中使用字节数组,我会导致 JNA 创建 C 字节数组的新副本。我想要做的是有一个指向 C 字节数组的指针,因此无需复制数据,因此使用 JNA 指针进行实​​验。

我的问题是为什么我不能从指针获取字节数组?任何帮助将不胜感激:)

(注意:让这变得更加困难的是我无法访问 C 源代码!!)

I'm working on a project at the moment that requires me to receive a call in Java from a C library. Basically I call a C function that takes a function pointer, the C function then uses the function pointer as a callback. I'm using JNA to pass a Java object (I'll call it the Callback object from now on) as the callback function. The callback object has a single method that receives a C structure (called Frame) that contains a 16 element byte array as well as other variables. I've wrapped this structure in a Java class in the standard JNA way, like this:

class Frame extends Structure {
    public short port;
    public short flags;
    public Pointer name // this is a pointer to the byte array
    public int rateDivisor;
}

The callback mechanism works fine! The callback object receives a Frame object from C, but when I try to get the byte array from the Pointer using name.getByteArray(0, 16) the application crashes with an Access Violation Exception. However, if I replace the Pointer with a byte array:

class Frame extends Structure {
    public short port;
    public short flags;
    public byte[] name = new byte[16];
    public int rateDivisor;
}

Then the code works fine! There's a good reason why I don't want to use this code however. Every time I call the function the returned Frame is actually the same object (it's just being reused) but the byte array is a new object. This callback function is being called many times a second which causes the garbage collector to goes crazy gobbling up thousands of temporary arrays. This project is performance critical so I really don't want any temporary objects in this part of the application.

My guess is that by using a byte array in the Frame class I'm causing JNA to create a new copy of the C byte array. What I want to do is have a pointer to the C byte array therefore removing the need to copy the data, hence the experimentation with JNA Pointer.

My question is why can't I get the byte array from the Pointer? Any help would be much appreciated :)

(Note: The thing that makes this even more difficult is that I don't have access to the C source code!!)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

小嗲 2024-12-14 09:04:03

原因可能很简单:name不是 C 结构对应项中的指针

让我向您展示两个示例:

C:

typedef struct{
  char * name;
}MyCStruct;

映射到 Java :

class MyJStruct extends Structure{
  Pointer name;
}

但是在另一种情况下(我认为您自己陷入了这种情况):

C:

typedef struct{
  char name[16];
}MyCStruct;

映射到 Java :

class MyJStruct extends Structure{
  byte[] name = new byte[16];
}

在第一种情况下,C 结构保存一个指针,它的 sizeof 是指针的大小(4 或 8 字节,取决于 32/64 位),在第二种情况下,它保存整个数组,这意味着它的大小为 16 字节。

这解释了两个 Java 映射之间的区别:JNA 需要知道如何读取结构字段,它也解释了为什么在调用 name.getByteArray(0, 16) 时会出现错误,因为这会执行以下:

  1. flags字段后面的前4个字节,
  2. 将其视为一个指针,
  3. 进入ram指向指向的地址,并读取16个字节

,由于指向的内存区域可能超出了程序的范围,因此崩溃了。

您可以在 C 中自行检查:如果 sizeof(struct Frame) 是 24,则该结构保存整个数组,如果它是 12(或 16),则它保存一个 32 位指针(或 64 位指针) )

关于这个问题的官方文档非常清楚,但很难找到,所以在这里:

http://twall.github.com/jna/3.3.0/javadoc /overview-summary.html

你会在“嵌套数组”下找到它,我真的鼓励你阅读“可变大小结构”下面的部分>”

希望这有

帮助

The reason is probably as simple as that : name is not a pointer in the C structure counterpart.

Let me show you two examples :

C:

typedef struct{
  char * name;
}MyCStruct;

maps to Java :

class MyJStruct extends Structure{
  Pointer name;
}

But in this other scenario (in which I think you got yourself into):

C:

typedef struct{
  char name[16];
}MyCStruct;

maps to Java :

class MyJStruct extends Structure{
  byte[] name = new byte[16];
}

In the first case the C structure holds a pointer and it's sizeof is the size of a pointer (4 or 8Bytes depending on 32/64bits), in the second case it holds the whole array which mean it's size is 16 Bytes.

This explains the difference between the two Java mappings : JNA needs to know how to read the structure fields and it explains too why you get an error while calling name.getByteArray(0, 16) as this exectute the following :

  1. take the first 4 Bytes following the flags field
  2. treat it as a pointer
  3. go into ram to the pointed adress and read 16 Bytes

Which crashed since the memory zone pointed is probably out of program reach.

You can check it yourself in C : if sizeof(struct Frame) is 24 then the struct holds the whole array if it's 12 (or 16) then it holds a 32 bits pointer (or a 64 bits pointer)

Official documentation about this matter is pretty clear but hard to find, so here you go :

http://twall.github.com/jna/3.3.0/javadoc/overview-summary.html

you'll find about it under "Nested arrays" and I really encourage you to read the section just below "Variable-sized structures"

hope this helps

regards

戏剧牡丹亭 2024-12-14 09:04:03

将回调的输入参数更改为 Pointer。维护您自己的私有指针->框架映射(有或没有弱引用),并且仅根据输入指针创建一个新框架(如果还没有)。

例如

Map frames = new HashMap<Pointer,Frame>();

void callback(Pointer p) {
   Frame f = frames.get(p);
   if (f == null) {
       f = new Frame(p);
       frames.put(p, f);
   }
   // do whatever...
}

Change the input parameter of your callback to Pointer. Maintain your own private Pointer->Frame map (with or without weak references), and only create a new Frame based on the input pointer if there isn't one already.

e.g.

Map frames = new HashMap<Pointer,Frame>();

void callback(Pointer p) {
   Frame f = frames.get(p);
   if (f == null) {
       f = new Frame(p);
       frames.put(p, f);
   }
   // do whatever...
}
隱形的亼 2024-12-14 09:04:03

(满足 StackTrace 期望的新答案;请参阅我之前的答案的评论)

好吧,我不确定是否有一个好的和干净的方法来做到这一点......因此我会指出一个很好的方法名为MemoryPointer 游戏的游戏

请记住,您可能需要这些方式(C):

Frame nativeLibFunc(...);        //1
Frame * nativeLibFunc(...);      //2
void nativeLibFunc(Frame f);     //3
void nativeLibFunc(Frame * fptr);//4

我认为最糟糕的情况是第三种,因为您不能这样做它没有之前解释过的法线贴图。下一个最坏的情况是第一种,因为您不会在 RAM 中获得位置,而是在堆栈顶部获得直接结果,因此读取它需要您欺骗 JNA 才能获取指针并读取内存区域之后的内容你想跳过(Java):

class MyMicroFrame extends Structure {
    public short port;
    public short flags;
    //public byte[] name = new byte[16]; ==> commented out, ignored
    //public int rateDivisor;            ==> commented out, read manually, see below
}
//...
MyMicroFrame mmf = NativeLib.INSTANCE.nativeLibFunc(...);
int rateDivisor = mmf.getPointer().getInt(20);

我想你明白了。如果您的结构较大,只需将其分成小部分,避免您想要跳过的区域,然后通过手动导航第一部分指向的内存来重新加入它们。

以这种方式工作时要记住的事情:

  1. 指针大小 32/64 位 - 4 或 8 字节,请参见 com.sun.jna.Native.POINTER_SIZE
  2. 结构对齐 我没有对此答案进行检查

(New answer to meet StackTrace expectations ; see comment to my previous answer)

Well I'm not sure if there is a nice and clean way to do it ... hence I'll point out a nice game called the Memory and Pointer game

Keep in mind you might need it these ways (C) :

Frame nativeLibFunc(...);        //1
Frame * nativeLibFunc(...);      //2
void nativeLibFunc(Frame f);     //3
void nativeLibFunc(Frame * fptr);//4

The worst case in my opinion will be the third since you can't do it without the normal mapping explained before. Next worst case is the first since you won't get a location in RAM but the direct result at the top of the stack, hence reading it requires you to trick JNA to be able to fetch the Pointer and read what comes after the memory zone you want to skip (Java):

class MyMicroFrame extends Structure {
    public short port;
    public short flags;
    //public byte[] name = new byte[16]; ==> commented out, ignored
    //public int rateDivisor;            ==> commented out, read manually, see below
}
//...
MyMicroFrame mmf = NativeLib.INSTANCE.nativeLibFunc(...);
int rateDivisor = mmf.getPointer().getInt(20);

I think you got the idea. if your structure is bigger, just split it in small parts avoiding the zones you want to skip an re-join them by manually navigating in the memory pointed by the first part.

Things to keep in mind while working this way :

  1. Pointer size 32/64 bits -- 4 or 8 Bytes see com.sun.jna.Native.POINTER_SIZE
  2. Structure alignement which I've not checked on this answer
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文