java 之JNA中的Memory和Pointer的使用方法
簡介
我們知道在native的代碼中有很多指針,這些指針在JNA中被映射成為Pointer。除了Pointer之外,JNA還提供了更加強(qiáng)大的Memory類,本文將會(huì)一起探討JNA中的Pointer
和Memory
的使用。
Pointer
Pointer是JNA中引入的類,用來表示native方法中的指針。大家回想一下native方法中的指針到底是什么呢?
native方法中的指針實(shí)際上就是一個(gè)地址,這個(gè)地址就是真正對象的內(nèi)存地址。所以在Pointer中定義了一個(gè)peer屬性,用來存儲(chǔ)真正對象的內(nèi)存地址:
protected long peer;
實(shí)時(shí)上,Pointer的構(gòu)造函數(shù)就需要傳入這個(gè)peer參數(shù):
public Pointer(long peer) { this.peer = peer; }
接下來我們看一下如何從Pointer中取出一個(gè)真正的對象,這里以byte數(shù)組為例:
public void read(long offset, byte[] buf, int index, int length) { Native.read(this, this.peer, offset, buf, index, length); }
實(shí)際上這個(gè)方法調(diào)用了Native.read方法,我們繼續(xù)看一下這個(gè)read方法:
static native void read(Pointer pointer, long baseaddr, long offset, byte[] buf, int index, int length);
可以看到它是一個(gè)真正的native方法,用來讀取一個(gè)指針對象。
除了Byte數(shù)組之外,Pointer還提供了很多其他類型的讀取方法。
又讀取就有寫入,我們再看下Pointer是怎么寫入數(shù)據(jù)的:
public void write(long offset, byte[] buf, int index, int length) { Native.write(this, this.peer, offset, buf, index, length); }
同樣的,還是調(diào)用 Native.write
方法來寫入數(shù)據(jù)。
這里Native.write方法也是一個(gè)native方法:
static native void write(Pointer pointer, long baseaddr, long offset, byte[] buf, int index, int length);
Pointer還提供了很多其他類型數(shù)據(jù)的寫入方法。
當(dāng)然還有更加直接的get*方法:
public byte getByte(long offset) { return Native.getByte(this, this.peer, offset); }
特殊的Pointer:Opaque
在Pointer中,還有兩個(gè)createConstant方法,用來創(chuàng)建不可讀也不可寫的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); }
實(shí)際上返回的而是Opaque類,這個(gè)類繼承自Pointer,但是它里面的所有read或者write方法,都會(huì)拋出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方法分配的內(nèi)存空間而言,除了Pointer指針的開始位置之外,我們還需要知道分配的空間大小。所以一個(gè)簡單的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個(gè)數(shù)據(jù),我們接下來一一進(jìn)行介紹。
首先是最為重要的size,size表示的是Memory中內(nèi)存空間的大小,我們來看下Memory的構(gòu)造函數(shù):
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類型的數(shù)據(jù)需要傳入一個(gè)size參數(shù),表示Memory占用的空間大小。當(dāng)然,這個(gè)size必須要大于0.
然后調(diào)用native方法的malloc方法來分配一個(gè)內(nèi)存空間,返回的peer保存的是內(nèi)存空間的開始地址。如果peer==0,表示分配失敗。
如果分配成功,則將當(dāng)前Memory保存到LinkedReference中,用來跟蹤當(dāng)前的位置。
我們可以看到Memory中有兩個(gè)LinkedReference,一個(gè)是HEAD,一個(gè)是reference。
LinkedReference
本身是一個(gè)WeakReference
,weekReference引用的對象只要垃圾回收執(zhí)行,就會(huì)被回收,而不管是否內(nèi)存不足。
private static class LinkedReference extends WeakReference<Memory>
我們看一下LinkedReference的構(gòu)造函數(shù):
private LinkedReference(Memory referent) { super(referent, QUEUE); }
這個(gè)QUEUE是ReferenceQueue,表示的是GC待回收的對象列表。
我們看到Memory的構(gòu)造函數(shù)除了設(shè)置size之外,還調(diào)用了:
reference = LinkedReference.track(this);
仔細(xì)看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; }
這個(gè)方法的意思是首先從QUEUE中拿出那些準(zhǔn)備被垃圾回收的Memory對象,然后將其從LinkedReference中unlink。 最后將新創(chuàng)建的對象加入到LinkedReference中。
因?yàn)镸emory中的QUEUE和HEAD都是類變量,所以這個(gè)LinkedReference
保存的是JVM中所有的Memory對象。
最后Memory中也提供了對應(yīng)的read和write方法,但是Memory中的方法和Pointer不同,Memory中的方法多了一個(gè)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); }
為什么會(huì)有boundsCheck呢?這是因?yàn)镸emory和Pointer不同,Memory中有一個(gè)size的屬性,用來存儲(chǔ)分配的內(nèi)存大小。使用boundsCheck就是來判斷訪問的地址是否出界,用來保證程序的安全。
總結(jié)
Pointer和Memory算是JNA中的高級功能,大家如果想要和native的alloc方法進(jìn)行映射的話,就要考慮使用了。
到此這篇關(guān)于java 之JNA中的Memory和Pointer的使用方法的文章就介紹到這了,更多相關(guān) Memory和Pointer內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- JAVA?POI設(shè)置EXCEL單元格格式用法舉例
- Java?POI導(dǎo)出Excel時(shí)合并單元格沒有邊框的問題解決
- java?poi?讀取單元格null或者空字符串方式
- java POI 如何實(shí)現(xiàn)Excel單元格內(nèi)容換行
- Java中EasyPoi導(dǎo)出復(fù)雜合并單元格的方法
- Java用POI解析excel并獲取所有單元格數(shù)據(jù)的實(shí)例
- Java使用POI實(shí)現(xiàn)導(dǎo)出Excel的方法詳解
- java 使用POI合并兩個(gè)word文檔
- java使用poi在excel單元格添加超鏈接設(shè)置字體顏色的方法
相關(guān)文章
java阻塞隊(duì)列BlockingQueue詳細(xì)解讀
這篇文章主要介紹了java阻塞隊(duì)列BlockingQueue詳細(xì)解讀,在新增的Concurrent包中,BlockingQueue很好的解決了多線程中,如何高效安全“傳輸”數(shù)據(jù)的問題,通過這些高效并且線程安全的隊(duì)列類,為我們快速搭建高質(zhì)量的多線程程序帶來極大的便利,需要的朋友可以參考下2023-10-10Spring Boot學(xué)習(xí)入門之統(tǒng)一異常處理詳解
我們在做Web應(yīng)用的時(shí)候,請求處理過程中發(fā)生錯(cuò)誤是非常常見的情況。下面這篇文章主要給大家介紹了關(guān)于Spring Boot學(xué)習(xí)入門之統(tǒng)一異常處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。2017-09-09SpringBoot Entity中枚舉類型詳細(xì)使用介紹
本文介紹SpringBoot如何在Entity(DAO)中使用枚舉類型。(本文使用MyBatis-Plus)。在實(shí)際開發(fā)中,經(jīng)常會(huì)遇到表示類型或者狀態(tài)的情況,比如:有三種支付方式:微信、支付寶、銀聯(lián)。本文介紹如何這種場景的方案對比,并用實(shí)例來介紹如何用枚舉這種最優(yōu)雅的來表示2022-10-10Spring使用IOC與DI實(shí)現(xiàn)完全注解開發(fā)
IOC也是Spring的核心之一了,之前學(xué)的時(shí)候是采用xml配置文件的方式去實(shí)現(xiàn)的,后來其中也多少穿插了幾個(gè)注解,但是沒有說完全采用注解實(shí)現(xiàn)。那么這篇文章就和大家分享一下,全部采用注解來實(shí)現(xiàn)IOC + DI2022-09-09