我的目标是创建一个数据结构,它应该在内存中具有以下表示:
int countOfElements; // 4 bytes
int time_1; // 4 bytes
double price_1; // 8 bytes
int time_2; // 4 bytes
double price_2; // 8 bytes
int time_3; // 4 bytes
double price_3; // 8 bytes
. . . . .
int time_n; // 4 bytes, n = countOfElements
double price_n; // 8 bytes, n = countOfElements
不幸的是,我不能自由选择这个结构在内存中的表示(字段顺序和类型),因为它与操作系统上的另一个进程有关。该数据结构打算通过使用WinAPI函数放置在共享内存上,该数据结构的目的是数据交换。因此,我还需要在java中获得指向该数据结构的指针…因此,我实现这个数据结构的努力看起来像:
import java.util.List;
import java.util.Arrays;
import com.sun.jna.Structure;
public class TQuote extends Structure {
public int time;
public double price;
@Override
protected List getFieldOrder() {
return Arrays.asList(new String[]{"time", "price"});
}
}
public class TSharedArray extends Structure {
public int countOfElements;
public TQuote[] elements;
public TSharedArray(int size) {
super();
countOfElements = size;
elements = new TQuote[size];
for (int i = 0; i < size; i++) elements[i] = new TQuote();
}
@Override
protected List getFieldOrder() {
return Arrays.asList(new String[]{"countOfElements", "elements"});
}
}
虽然,我试图实例化结构TSharedArray structure = new TSharedArray(10);
抛出了一个异常:
/*
Invalid Structure field in class xxx$TSharedArray, field name 'elements' (class xxx$TQuote): Can't instantiate class xxx$TQuote (java.lang.InstantiationException: xxx$TQuote)
java.lang.IllegalArgumentException: Invalid Structure field in class xxx$TSharedArray, field name 'elements' (class xxx$TQuote): Can't instantiate class xxx$TQuote (java.lang.InstantiationException: xxx$TQuote)
at com.sun.jna.Structure.validateField(Structure.java:962)
at com.sun.jna.Structure.validateField(Structure.java:954)
at com.sun.jna.Structure.validateFields(Structure.java:972)
at com.sun.jna.Structure.<init>(Structure.java:186)
at com.sun.jna.Structure.<init>(Structure.java:180)
at com.sun.jna.Structure.<init>(Structure.java:167)
at com.sun.jna.Structure.<init>(Structure.java:159)
at xxx$TSharedArray.<init>(xxx.java:35)
at xxx.onStart(xxx.java:57)
at java.lang.Thread.run(Unknown Source)
*/
Java代码xxx.java行#35是super();
任何想法,如何解决它?
此外,我注意到一件奇怪的事情……假设我们有三个结构体,每个结构体包含两个字段:
public class TIntInt extends Structure {
public int int1;
public int int2;
@Override
protected List getFieldOrder() {
return Arrays.asList(new String[]{"int1", "int2"});
}
}
public class TDblDbl extends Structure {
public double dbl1;
public double dbl2;
@Override
protected List getFieldOrder() {
return Arrays.asList(new String[]{"dbl1", "dbl2"});
}
}
public class TIntDbl extends Structure {
public int int1;
public double dbl1;
@Override
protected List getFieldOrder() {
return Arrays.asList(new String[]{"int1", "dbl1"});
}
}
第一个(TIntInt)的大小如预期的2*4=8字节,第二个(TDblDbl)的大小如预期的2*8=16字节,但第三个(TIntDbl -它与TQuote相同)的大小为16字节,而不是预期的4+8=12。为什么?:
System.out.println("TIntInt.size()=" + new TIntInt().size()); // 8 bytes, Ok
System.out.println("TDblDbl.size()=" + new TDblDbl().size()); // 16 bytes, Ok
System.out.println("TIntDbl.size()=" + new TIntDbl().size()); // 16 bytes!!! Why?
Structure
中的任何数组字段都必须初始化。注意,不支持数组大小为0。
public class TSharedArray extends Structure {
public int countOfElements;
public TQuote[] elements = new TQuote[1];
public TSharedArray() {
this(1);
}
public TSharedArray(Pointer p) {
super(p);
countOfElements = (int)readField("countOfElements");
elements = new TQuote[countOfElements];
read();
}
public TSharedArray(int size) {
countOfElements = size;
elements = (TQuote[])new TQuote().toArray(size);
}
@Override
protected List getFieldOrder() {
return Arrays.asList(new String[]{"countOfElements", "elements"});
}
}
关于你的第二个问题,大小的差异是由于结构填充。通常字段将在与其大小相等的内存边界上对齐,因此在您的int
字段之后将有4字节填充,以便在8字节边界上对齐double
字段。
编辑
任何用作字段的结构都必须有一个公共的、无参数的构造函数(而且最好有一个基于指针的构造函数)。您看到的错误是因为JNA无法通过反射创建TQuote
的实例。您需要解决这个问题,或者显式地为该字段创建一个预填充的数组:
public TQuote[] elements = (TQuote[])new TQuote().toArray(1);
对齐
如果你想避免默认的平台对齐(参见Structure.setAlignType()
),你可以使用Structure.ALIGN_NONE
。
使用的内存量以8字节块的形式增加。有关详细信息,请参阅https://stackoverflow.com/a/321436/3915166
经过多次尝试和实验,我发现唯一没有异常运行的代码是使用Structure.ByReference
:
public class TQuote extends Structure {
public class ByReference extends TQuote implements Structure.ByReference {}
public int time;
public double price;
@Override
protected List getFieldOrder() {
return Arrays.asList("time", "price");
}
}
public class TSharedArray extends Structure {
public int countOfElements;
public TQuote.ByReference[] elements;
public TSharedArray(int size) {
super();
countOfElements = size;
elements = new TQuote.ByReference[size];
}
@Override
protected List getFieldOrder() {
return Arrays.asList("countOfElements", "elements");
}
}
但是,上面的代码没有达到我的目标,因为它形成了一个引用数组而不是值数组。因此,该结构的内存表示是不合适的。
我放弃了试图用结构来解决问题…现在我有了另一个不使用结构的解决方案:我在指针的帮助下创建了所需的内存分配。