您好,登錄后才能下訂單哦!
這篇文章主要講解了“JNA中的Memory和Pointer怎么使用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“JNA中的Memory和Pointer怎么使用”吧!
我們知道在native的代碼中有很多指針,這些指針在JNA中被映射成為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中,還有兩個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); }
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就是來判斷訪問的地址是否出界,用來保證程序的安全。
感謝各位的閱讀,以上就是“JNA中的Memory和Pointer怎么使用”的內容了,經過本文的學習后,相信大家對JNA中的Memory和Pointer怎么使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。