java 之JNA中的Memory和Pointer的使用方法

目录
  • 简介
  • Pointer
  • 特殊的Pointer:Opaque
  • Memory
  • 总结

简介

我们知道在native的代码中有很多指针,这些指针在JNA中被映射成为Pointer。除了Pointer之外,JNA还提供了更加强大的Memory类,本文将会一起探讨JNA中的PointerMemory的使用。

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方法进行映射的话,就要考虑使用了。

到此这篇关于java 之JNA中的Memory和Pointer的使用方法的文章就介绍到这了,更多相关 Memory和Pointer内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 解决java.lang.NullPointerException报错以及分析出现的几种原因

    java.lang.NullPointerException出现的几种原因: 1.字符串变量未初始化 2.接口类型的对象没有用具体的类初始化,比如: Map map // 会报错 Map map = new Map(); //则不会报错了 3.当一个对象的值为空时,你没有判断为空的情况. 4.字符串与文字的比较,文字可以是一个字符串或Enum的元素,如下会出现异常 String str = null; if(str.equals("Test")){ //这里的代码将不会被触发,因为会抛

  • java静态工具类注入service出现NullPointerException异常处理

    目录 一般我们在controller层调用service时,只需要使用@Autowired注解即可,例如如下代码我们经常看到: @RestController @RequestMapping("business") public class BizResourceController { @Autowired private BusinessService businessService; @RequestMapping(path = "/queryYearList"

  • java高级用法之JNA中的Function

    目录 简介 function的定义 Function的实际应用 总结 简介 在JNA中,为了和native的function进行映射,我们可以有两种mapping方式,第一种是interface mapping,第二种是direct mapping.虽然两种方式不同,但是在具体的方法映射中,我们都需要在JAVA中定义一个和native方法进行映射的方法. 而这个JAVA中的映射在JNA中就是一个function.通过或者function对象,我们可以实现一些非常强大的功能,一起看看吧. func

  • java高级用法之JNA中的Structure

    目录 简介 native中的struct Structure 特殊类型的Structure 结构体数组作为参数 结构体数组作为返回值 结构体中的结构体 结构体中的数组 结构体中的可变字段 结构体中的只读字段 总结 简介 前面我们讲到了JNA中JAVA代码和native代码的映射,虽然可以通过TypeMapper来将JAVA中的类型和native中的类型进行映射,但是native中的数据类型都是基础类型,如果native中的数据类型是复杂的struct类型该如何进行映射呢? 不用怕,JNA提供了S

  • java.lang.NullPointerException异常问题解决方案

    java.lang.NullPointerException异常原因是因为创建了一个引用类型的变量却没有指向任何对象而又去通过这个引用类型变量加点的形式去访问非静态的方法及属性. 给出三种情况, 第一种情况,在启动类中定义了引用类型变量,赋值为空: /** * 引用类型变量没有指向对象所引起的空指针异常 * @author Superhero * @version 2018年12月16日上午10:32:43 */ //图书类 class Books { private String name;

  • 详解Java中NullPointerException异常的原因详解以及解决方法

    NullPointerException是当您尝试使用指向内存中空位置的引用(null)时发生的异常,就好像它引用了一个对象一样. 当我们声明引用变量(即对象)时,实际上是在创建指向对象的指针.考虑以下代码,您可以在其中声明基本类型的整型变量x: int x; x = 10; 在此示例中,变量x是一个整型变量,Java将为您初始化为0.当您在第二行中将其分配给10时,值10将被写入x指向的内存中. 但是,当您尝试声明引用类型时会发生不同的事情.请使用以下代码: Integer num; num

  • java 之JNA中的Memory和Pointer的使用方法

    目录 简介 Pointer 特殊的Pointer:Opaque Memory 总结 简介 我们知道在native的代码中有很多指针,这些指针在JNA中被映射成为Pointer.除了Pointer之外,JNA还提供了更加强大的Memory类,本文将会一起探讨JNA中的Pointer和Memory的使用. Pointer Pointer是JNA中引入的类,用来表示native方法中的指针.大家回想一下native方法中的指针到底是什么呢? native方法中的指针实际上就是一个地址,这个地址就是真正

  • java、js中实现无限层级的树形结构方法(类似递归)

    js中: var zNodes=[ {id:0,pId:-1,name:"Aaaa"}, {id:1,pId:0,name:"A"}, {id:11,pId:1,name:"A1"}, {id:12,pId:1,name:"A2"}, {id:13,pId:1,name:"A3"}, {id:2,pId:0,name:"B"}, {id:21,pId:2,name:"B1&qu

  • Java在Word中插入上标和下标的实现方法

    目录 前言 程序环境配置 安装Spire.Doc for Java 使用Java在Word中插入上标和下标 步骤 代码实现 效果图 在某些情况下,你可能需要在Microsoft Word中插入上标和下标.例如,当你正在创建一个涉及科学公式的学术文件时. 前言 在某些情况下,你可能需要在Microsoft Word中插入上标和下标.例如,当你正在创建一个涉及科学公式的学术文件时.在这篇文章中,你将学习如何使用Spire.Doc for Java库在Word文档中插入上标和下标. 程序环境配置 安装

  • Java在PowerPoint中添加上标和下标的实现方法

    目录 前言 程序环境配置 安装Spire.Presentation for Java 添加上标和下标 代码实现 前言 当我们在演示文稿中添加商标.版权或其他符号时,我们可能希望该符号出现在某个文本的上方或下方.在Microsoft PowerPoint中,我们可以通过对符号应用上标或下标格式来实现这种效果.在这篇文章中,我们将演示如何在Java中使用Spire.Presentation for Java以编程的方式实现这一任务. 程序环境配置 安装Spire.Presentation for J

  • 使用Java获取html中Select,radio多选的值方法

    复制代码 代码如下: //jsp中的select多选代码<select name="selectvalues" size="4" multiple="multiple">  <option value="volvo">Volvo</option>  <option value="saab">Saab</option>  <option val

  • Java多线程编程中使用Condition类操作锁的方法详解

    Condition的作用是对锁进行更精确的控制.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法.不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的:而Condition是需要与"互斥

  • java高级用法之JNA中的回调问题

    目录 简介 JNA中的Callback callback的应用 callback的定义 callback的获取和应用 在多线程环境中使用callback 总结 简介 什么是callback呢?简单点说callback就是回调通知,当我们需要在某个方法完成之后,或者某个事件触发之后,来通知进行某些特定的任务就需要用到callback了. 最有可能看到callback的语言就是javascript了,基本上在javascript中,callback无处不在.为了解决callback导致的回调地狱的问

  • 详解JNA中的回调方法

    目录 简介 JNA 中的 Callback callback 的应用 callback 的定义 callback 的获取和应用 在多线程环境中使用 callback 总结 简介 什么是 callback 呢?简单点说 callback 就是回调通知,当我们需要在某个方法完成之后,或者某个事件触发之后,来通知进行某些特定的任务就需要用到 callback 了. 最有可能看到 callback 的语言就是 javascript 了,基本上在 javascript 中,callback 无处不在.为了

  • Java高级用法中的JNA类型映射注意细节及使用问题

    目录 简介 String Buffers,Memory,数组和Pointer 可变参数 总结 简介 JNA提供JAVA类型和native类型的映射关系,但是这一种映射关系只是一个大概的映射,我们在实际的应用中还有很多需要注意的事项,本文将会为大家详细讲解在使用类型映射中可能会出现的问题.一起来看看吧. String 首先是String的映射,JAVA中的String实际上对应的是两种native类型:const char* 和 const wchar_t*.默认情况下String会被转换成为ch

随机推荐