是什么导致“无法从ByteBuffer对象中检索本机地址”?

时间:2010-12-26 16:38:50

标签: java java-native-interface bytebuffer

作为一名非常新手的Java程序员,我可能不应该惹这样的事情。不幸的是,我正在使用一个库,它有一个接受ByteBuffer对象的方法,并在我尝试使用它时抛出:

Exception in thread "main" java.lang.NullPointerException: Unable to retrieve native address from ByteBuffer object

是因为我使用的是非直接缓冲区吗?

编辑: 那里的代码不多。我正在使用的库是jNetPcap,我正在尝试将数据包转储到文件中。我的代码获取现有数据包,并从中提取ByteBuffer:

byte[] bytes = m_packet.getByteArray(0, m_packet.size());
ByteBuffer buffer = ByteBuffer.wrap(bytes);

然后它调用了带有ByteBuffer的jNetPcap的转储方法。

3 个答案:

答案 0 :(得分:1)

根据您提供的信息,您似乎正在使用ByteBuffer实现,该实现不允许Native代码访问底层内存结构。它试图访问ByteBuffer中的直接内存,它可能不应该这样做,并且因为从ByteBuffer派生的类不直接存储数据而失败。

如果这是关键代码,你无法改变,最好的办法是使用Java实现创建一个ByteBuffer,然后将原始数据复制到临时缓冲区中;将新缓冲区传递给您的本机方法。然后,我将分析代码以查看它是否对性能产生影响。

以下是如何执行此操作的示例。我对使用rewind()limit()有点犹豫,因为我不知道你的ByteBuffer的实现会返回什么,所以请检查以确保它正确实现了ByteBuffer的接口。

此代码故意非法访问索引3以显示未添加额外数据。

public static void main(String[] args) {

    // This will be your implementation of ByteBuffer that
    // doesn't allow direct access.
    ByteBuffer originalBuffer = ByteBuffer.wrap(new byte[]{12, 50, 70});


    originalBuffer.rewind();
    byte[] newArray = new byte[originalBuffer.limit()];
    originalBuffer.get(newArray, 0, newArray.length);

    ByteBuffer newBuffer = ByteBuffer.wrap(newArray);

    System.out.println("Limit: " + newBuffer.limit());
    System.out.println("Index 0: " + newBuffer.get(0));
    System.out.println("Index 1: " + newBuffer.get(1));
    System.out.println("Index 2: " + newBuffer.get(2));
    System.out.println("Index 3: " + newBuffer.get(3));
}

输出:

限制:3

索引0:12

指数1:50

指数2:70

线程“main”中的异常java.lang.IndexOutOfBoundsException

    at java.nio.Buffer.checkIndex(Buffer.java:514)

    at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:121)

    at stackoverflow_4534583.Main.main(Main.java:35)

答案 1 :(得分:1)

wrap不会创建“直接”字节缓冲区。直接字节缓冲区通常来自使用内存映射API。编写您正在使用的JNI代码的人并不善待您,因为他们没有编写代码来容忍非直接缓冲区。

然而,一切都没有丢失:http://download.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html#allocateDirect(int

会做你需要的。

答案 2 :(得分:1)

许多JNI调用都期望直接使用ByteBuffer。即使是Oracle Java 6.0中的标准库也希望如此,如果您为它们提供堆ByteBuffer,它们会为您直接复制数据。在您的情况下,您有一个byte []可以复制到直接的ByteBuffer。注意:创建一个直接的ByteBuffer是很昂贵的,如果可以,你应该缓存/回收它们。

// the true size is always a multiple of a page anyway.
static final ByteBuffer buffer = ByteBuffer.allocateDirect(4096); 

// synchronize the buffer if you need to, or use a ThreadLocal buffer as a simple cache.
byte[] bytes = m_packet.getByteArray(0, m_packet.size());
buffer.clear();
buffer.put(bytes);
buffer.flip();