简介
咱们晓得在native的代码中有很多指针,这些指针在JNA中被映射成为Pointer。除了Pointer之外,JNA还提供了更加弱小的Memory类,本文将会一起探讨JNA中的Pointer和Memory的应用。
Pointer
Pointer是JNA中引入的类,用来示意native办法中的指针。大家回忆一下native办法中的指针到底是什么呢?
native办法中的指针实际上就是一个地址,这个地址就是真正对象的内存地址。所以在Pointer中定义了一个peer属性,用来存储真正对象的内存地址:
protected long peer;
实时上,Pointer的构造函数就须要传入这个peer参数:
public Pointer(long peer) {
this.peer = peer;
}
接下来咱们看一下如何从Pointer中取出一个真正的对象,这里以byte数组为例:
public void read(long offset, byte[] buf, int index, int length) {
Native.read(this, this.peer, offset, buf, index, length);
}
实际上这个办法调用了Native.read办法,咱们持续看一下这个read办法:
static native void read(Pointer pointer, long baseaddr, long offset, byte[] buf, int index, int length);
能够看到它是一个真正的native办法,用来读取一个指针对象。
除了Byte数组之外,Pointer还提供了很多其余类型的读取办法。
又读取就有写入,咱们再看下Pointer是怎么写入数据的:
public void write(long offset, byte[] buf, int index, int length) {
Native.write(this, this.peer, offset, buf, index, length);
}
同样的,还是调用 Native.write办法来写入数据。
这里Native.write办法也是一个native办法:
static native void write(Pointer pointer, long baseaddr, long offset, byte[] buf, int index, int length);
Pointer还提供了很多其余类型数据的写入办法。
当然还有更加间接的get*办法:
public byte getByte(long offset) {
return Native.getByte(this, this.peer, offset);
}
非凡的Pointer:Opaque
在Pointer中,还有两个createConstant办法,用来创立不可读也不可写的Pointer:
public static final Pointer createConstant(long peer) {
return new Opaque(peer);
}
public static final Pointer createConstant(int peer) {
return new Opaque((long)peer & 0xFFFFFFFF);
}
实际上返回的而是Opaque类,这个类继承自Pointer,然而它外面的所有read或者write办法,都会抛出UnsupportedOperationException:
private static class Opaque extends Pointer {
private Opaque(long peer) { super(peer); }
@Override
public Pointer share(long offset, long size) {
throw new UnsupportedOperationException(MSG);
}
Memory
Pointer是根本的指针映射,如果对于通过应用native的malloc办法调配的内存空间而言,除了Pointer指针的开始地位之外,咱们还须要晓得调配的空间大小。所以一个简略的Pointer是不够用了。
这种状况下,咱们就须要应用Memory。
Memory是一种非凡的Pointer, 它保留了调配进去的空间大小。咱们来看一下Memory的定义和它外面蕴含的属性:
public class Memory extends Pointer {
...
private static ReferenceQueue<Memory> QUEUE = new ReferenceQueue<Memory>();
private static LinkedReference HEAD; // the head of the doubly linked list used for instance tracking
private static final WeakMemoryHolder buffers = new WeakMemoryHolder();
private final LinkedReference reference; // used to track the instance
protected long size; // Size of the malloc'ed space
...
}
Memory外面定义了5个数据,咱们接下来一一进行介绍。
首先是最为重要的size,size示意的是Memory中内存空间的大小,咱们来看下Memory的构造函数:
public Memory(long size) {
this.size = size;
if (size <= 0) {
throw new IllegalArgumentException("Allocation size must be greater than zero");
}
peer = malloc(size);
if (peer == 0)
throw new OutOfMemoryError("Cannot allocate " + size + " bytes");
reference = LinkedReference.track(this);
}
能够看到Memory类型的数据须要传入一个size参数,示意Memory占用的空间大小。当然,这个size必须要大于0.
而后调用native办法的malloc办法来调配一个内存空间,返回的peer保留的是内存空间的开始地址。如果peer==0,示意调配失败。
如果调配胜利,则将以后Memory保留到LinkedReference中,用来跟踪以后的地位。
咱们能够看到Memory中有两个LinkedReference,一个是HEAD,一个是reference。
LinkedReference自身是一个WeakReference,weekReference援用的对象只有垃圾回收执行,就会被回收,而不论是否内存不足。
private static class LinkedReference extends WeakReference<Memory>
咱们看一下LinkedReference的构造函数:
private LinkedReference(Memory referent) {
super(referent, QUEUE);
}
这个QUEUE是ReferenceQueue,示意的是GC待回收的对象列表。
咱们看到Memory的构造函数除了设置size之外,还调用了:
reference = LinkedReference.track(this);
认真看LinkedReference.track办法:
static LinkedReference track(Memory instance) {
// use a different lock here to allow the finialzier to unlink elements too
synchronized (QUEUE) {
LinkedReference stale;
// handle stale references here to avoid GC overheating when memory is limited
while ((stale = (LinkedReference) QUEUE.poll()) != null) {
stale.unlink();
}
}
// keep object allocation outside the syncronized block
LinkedReference entry = new LinkedReference(instance);
synchronized (LinkedReference.class) {
if (HEAD != null) {
entry.next = HEAD;
HEAD = HEAD.prev = entry;
} else {
HEAD = entry;
}
}
return entry;
}
这个办法的意思是首先从QUEUE中拿出那些筹备被垃圾回收的Memory对象,而后将其从LinkedReference中unlink。 最初将新创建的对象退出到LinkedReference中。
因为Memory中的QUEUE和HEAD都是类变量,所以这个LinkedReference保留的是JVM中所有的Memory对象。
最初Memory中也提供了对应的read和write办法,然而Memory中的办法和Pointer不同,Memory中的办法多了一个boundsCheck,如下所示:
public void read(long bOff, byte[] buf, int index, int length) {
boundsCheck(bOff, length * 1L);
super.read(bOff, buf, index, length);
}
public void write(long bOff, byte[] buf, int index, int length) {
boundsCheck(bOff, length * 1L);
super.write(bOff, buf, index, length);
}
为什么会有boundsCheck呢?这是因为Memory和Pointer不同,Memory中有一个size的属性,用来存储调配的内存大小。应用boundsCheck就是来判断拜访的地址是否出界,用来保障程序的平安。
总结
Pointer和Memory算是JNA中的高级性能,大家如果想要和native的alloc办法进行映射的话,就要思考应用了。
本文已收录于 http://www.flydean.com/06-jna-memory/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」,懂技术,更懂你!
发表回复