简介

我们知道在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/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

java高级用法之:JNA中的Memory和Pointer的更多相关文章

  1. java高级用法之:JNA中的Structure

    目录 简介 native中的struct Structure 特殊类型的Structure 结构体数组作为参数 结构体数组作为返回值 结构体中的结构体 结构体中的数组 结构体中的可变字段 结构体中的只 ...

  2. java高级用法之:JNA中的Function

    目录 简介 function的定义 Function的实际应用 总结 简介 在JNA中,为了和native的function进行映射,我们可以有两种mapping方式,第一种是interface ma ...

  3. java高级用法之:JNA中的回调

    目录 简介 JNA中的Callback callback的应用 callback的定义 callback的获取和应用 在多线程环境中使用callback 总结 简介 什么是callback呢?简单点说 ...

  4. java高级用法之:JNA类型映射应该注意的问题

    目录 简介 String Buffers,Memory,数组和Pointer 可变参数 总结 简介 JNA提供JAVA类型和native类型的映射关系,但是这一种映射关系只是一个大概的映射,我们在实际 ...

  5. java高级用法之:在JNA中将本地方法映射到JAVA代码中

    目录 简介 Library Mapping Function Mapping Invocation Mapping 防止VM崩溃 性能考虑 总结 简介 不管是JNI还是JNA,最终调用的都是nativ ...

  6. java高级用法之:在JNA中使用类型映射

    目录 简介 类型映射的本质 TypeMapper NativeMapped 总结 简介 JNA中有很多种映射,library的映射,函数的映射还有函数参数和返回值的映射,libary和函数的映射比较简 ...

  7. java高级用法之:调用本地方法的利器JNA

    目录 简介 JNA初探 JNA加载native lib的流程 本地方法中的结构体参数 总结 简介 JAVA是可以调用本地方法的,官方提供的调用方式叫做JNI,全称叫做java native inter ...

  8. java 高级用法整理

    一.retentionpolicy.class vs runtime区别 java5,增加了注解的功能:其中retentionpolicy注解的生命周期,提供了三种选择策略 source.class和 ...

  9. java高级用法之:无所不能的java,本地方法调用实况

    目录 简介 JDK的本地方法 自定义native方法 总结 简介 相信每个程序员都有一个成为C++大师的梦想,毕竟C++程序员处于程序员鄙视链的顶端,他可以俯视任何其他语言的程序员. 但事实情况是,无 ...

随机推荐

  1. 使用 JDBC 操作数据库时,如何提升读取数据的性能?如 何提升更新数据的性能?

    要提升读取数据的性能,可以指定通过结果集(ResultSet)对象的 setFetchSize() 方法指定每次抓取的记录数(典型的空间换时间策略):要提升更新数据的性能 可以使用 PreparedS ...

  2. Springboot 抛出Failed to determine a suitable driver class异常原因

    SpringBoot项目,已经依赖了MySQL驱动,却还是无法启动,通过问题排除,如果是启动项目,那么pom值 <packaging>pom</packaging> Faile ...

  3. 学习RabbitMQ(三)

    1 用户注册后(会立即提示注册成功),过一会发送短信和邮件通知 发布/订阅模型 以上模式一般是用户注册成功后,写入一条数据到mysql,在发送一条消息到MQ! 如果不用消息中间件(或者简单的做成异步发 ...

  4. AGENS算法

    3.2 层次方法 下图,上面是从左到右由5个簇逐渐合并成1个簇的过程,下面是从右到左由一个簇逐渐分裂成5个簇的过程 AGENS算法 最后面一句话是重点,假设有<A,B>,<C,D&g ...

  5. 决策树算法4:CHAID

    原理: 其中 n = a+b+c+d 卡方计算(例子)使用 sklearn完成 data.csv中的部分数据 #如何使用卡方检测相关度 from sklearn.feature_selection i ...

  6. 开源HTML5游戏引擎Kiwi.js 1.0正式发布

    Kiwi.js是由GameLab开发的一款全新的开源HTML5 JavaScript游戏引擎.在经过一年多的开发和测试之后,终于在日前正式发布了Kiwi.js 1.0版本. 其创始人Dan Milwa ...

  7. Amaze UI 模版中心上线丨十几款高质量优秀模版免费提供!

    Amaze UI模版中心终于上线了,目前汇聚了包含企业门户.新闻资讯.管理后台等多个领域的模版,全都可以免费下载. Amaze UI模版中心后续还会增加更多的模版以及领域,请各位持续关注. 模版中心的 ...

  8. CSS揭秘之《多重边框》

    1.box-shadow还接受第四个参数(称作"扩张半径"), 通过指定正值或负值, 可以让投影面积加大或者减小2.如果我们想要一道实线边框其实也是可以通过box-shadow来模 ...

  9. 设计模式之:工厂方法模式FactoryMethodPattern的实现

    本例用到了配置文件.接口.反射.多态: 满足的设计原则: 通过工厂,实现创建对象和使用对象的分离,实现松耦合,满足迪米特法则: 通过配置文件指定创建对象类型,而不需更改源代码,满足开闭原则: 容易实现 ...

  10. vue配置请求转发解决跨域问题

    通过nodejs的请求转发到后台,前端地址:http://localhost:8080   后端地址:http://localhost:8081 vue.config.js内容如下: let prox ...