无法在 java 中实例化结构内部的结构



我的目标是创建一个数据结构,它应该在内存中具有以下表示:

    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");
  }
}

但是,上面的代码没有达到我的目标,因为它形成了一个引用数组而不是值数组。因此,该结构的内存表示是不合适的。

我放弃了试图用结构来解决问题…现在我有了另一个不使用结构的解决方案:我在指针的帮助下创建了所需的内存分配。

最新更新