Java直接(堆外)内存使用详解

本篇主要讲解如何使用直接内存(堆外内存),并按照下面的步骤进行说明:html

相关背景-->读写操做-->关键属性-->读写实践-->扩展-->参考说明

但愿对想使用直接内存的朋友,提供点快捷的参考。java

数据类型

下面这些,都是在使用DirectBuffer中必备的一些常识,暂做了解吧!若是想要深刻理解,能够看看下面参考的那些博客。数组

基本类型长度

在Java中有不少的基本类型,好比:this

  • byte,一个字节是8位bit,也就是1B
  • short,16位bit,也就是2B
  • int,32位bit,也就是4B
  • long, 64位bit,也就是8B
  • char,16位bit,也就是2B
  • float,32位bit,也就是4B
  • double,64位bit,也就是8B

不一样的类型都会按照本身的位数来存储,而且能够自动进行转换提高。
bytecharshort均可以自动提高为int,若是操做数有long,就会自动提高为longfloatdouble也是如此。.net

大端小端

因为一个数据类型可能有不少个字节组成的,那么它们是如何摆放的。这个是有讲究的:指针

  • 大端:低地址位 存放 高有效字节
  • 小端:低地址位 存放 低有效字节

举个例子,一个char是有两个字节组成的,这两个字节存储可能会显示成以下的模样,好比字符a:code

低地址位    高地址位
大端;        00              96
小端:        96              00

String与new String的区别

再说说"hello"new String("hello")的区别:htm

若是是"hello",JVM会先去共享的字符串池中查找,有没有"hello"这个词,若是有直接返回它的引用;若是没有,就会建立这个对象,再返回。所以,"a"+"b"至关于存在3个对象,分别是"a""b""ab"对象

new String("hello"),则省去了查找的过程,直接就建立一个hello的对象,而且返回引用。blog

读写数据

在直接内存中,经过allocateDirect(int byte_length)申请直接内存。这段内存能够理解为一段普通的基于Byte的数组,所以插入和读取都跟普通的数组差很少。

只不过提供了基于不一样数据类型的插入方法,好比:

  • put(byte) 插入一个byte
  • put(byte[]) 插入一个byte数组
  • putChar(char) 插入字符
  • putInt(int) 插入Int
  • putLong(long) 插入long

等等....详细的使用方法,也能够参考下面的图片:

对应读取数据,跟写入差很少:

注意全部没有index参数的方法,都是按照当前position的位置进行操做的。

下面看看什么是position,还有什么其余的属性吧!

基本的属性值

它有几个关键的指标:

mark-->position-->limit-->capacity

另外,还有remaining=limit-position

先说说他们的意思吧!

当前位置——position

position是当前数组的指针,指示当前数据位置。举个例子:

ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
buffer.putChar('a');
System.out.println(buffer);
buffer.putChar('c');
System.out.println(buffer);
buffer.putInt(10);
System.out.println(buffer);

因为一个char是2个字节,一个Int是4个字节,所以position的位置分别是:

2,4,8

注意,Position的位置是插入数据的当前位置,若是插入数据,就会自动后移。
也就是说,若是存储的是两个字节的数据,position的位置是在第三个字节上,下标就是2。

java.nio.DirectByteBuffer[pos=2 lim=1024 cap=1024]
java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024]
java.nio.DirectByteBuffer[pos=8 lim=1024 cap=1024]
  • position能够经过position()得到,也能够经过position(int)设置。
//position(int)方法的源码
public final Buffer position(int newPosition) {
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }

注意:position的位置要比limit小,比mark大

空间容量——capacity

capacity是当前申请的直接内存的容量,它是申请后就不会改变的。

  • capacity则能够经过capacity()方法得到。

限制大小——limit

咱们可能想要改变这段直接内存的大小,所以能够经过一个叫作Limit的属性设置。

  • limit则能够经过limit()得到,经过limit(int)进行设置。

注意limit要比mark和position大,比capacity小。

//limit(int)方法的源码
public final Buffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
    }

标记位置——mark

mark,就是一个标记为而已,记录当前的position的值。经常使用的场景,就是记录某一次插入数据的位置,方便下一次进行回溯。

  • 能够使用mark()方法进行标记,
  • 使用reset()方法进行清除,
  • 使用rewind()方法进行初始化
//mark方法标记当前的position,默认为-1
public final Buffer mark() {
    mark = position;
    return this;
}
//reset方法重置mark的位置,position的位置,不能小于mark的位置,不然会出错
public final Buffer reset() {
    int m = mark;
    if (m < 0)
        throw new InvalidMarkException();
    position = m;
    return this;
}
//重置mark为-1.position为0
public final Buffer rewind() {
    position = 0;
    mark = -1;
    return this;
}

使用案例

ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
buffer.putChar('a');
buffer.putChar('c');
System.out.println("插入完数据 " + buffer);
buffer.mark();// 记录mark的位置
buffer.position(30);// 设置的position必定要比mark大,不然mark没法重置
System.out.println("reset前 " + buffer);
buffer.reset();// 重置reset ,reset后的position=mark
System.out.println("reset后 " + buffer);
buffer.rewind();//清除标记,position变成0,mark变成-1
System.out.println("清除标记后 " + buffer);

能够看到以下的运行结果:

插入完数据 java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024]
reset前 java.nio.DirectByteBuffer[pos=30 lim=1024 cap=1024]
reset后 java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024]
清除标记后 java.nio.DirectByteBuffer[pos=0 lim=1024 cap=1024]

剩余空间——remaing

remaing则表示当前的剩余空间:

public final int remaining() {
        return limit - position;
    }

读写实践

写操做主要就是按照本身的数据类型,写入到直接内存中,注意每次写入数据的时候,position都会自动加上写入数据的长度,指向下一个该写入的起始位置:

下面看看如何写入一段byte[]或者字符串:

ByteBuffer buffer = ByteBuffer.allocateDirect(10);
byte[] data = {1,2};
buffer.put(data);
System.out.println("写byte[]后 " + buffer);
buffer.clear();
buffer.put("hello".getBytes());
System.out.println("写string后 " + buffer);

输出的内容为:

写byte[]后 java.nio.DirectByteBuffer[pos=2 lim=10 cap=10]
写string后 java.nio.DirectByteBuffer[pos=5 lim=10 cap=10]

读的时候,能够经过一个外部的byte[]数组进行读取。因为没有找到直接操做直接内存的方法: 所以若是想在JVM应用中使用直接内存,须要申请一段堆中的空间,存放数据。

若是有更好的方法,还请留言。

ByteBuffer buffer = ByteBuffer.allocateDirect(10);
buffer.put(new byte[]{1,2,3,4});
System.out.println("刚写完数据 " +buffer);
buffer.flip();
System.out.println("flip以后 " +buffer);
byte[] target = new byte[buffer.limit()];
buffer.get(target);//自动读取target.length个数据
for(byte b : target){
    System.out.println(b);
}
System.out.println("读取完数组 " +buffer);

输出为

刚写完数据 java.nio.DirectByteBuffer[pos=4 lim=10 cap=10]
flip以后 java.nio.DirectByteBuffer[pos=0 lim=4 cap=10]
1
2
3
4
读取完数组 java.nio.DirectByteBuffer[pos=4 lim=4 cap=10]

经常使用方法

上面的读写例子中,有几个经常使用的方法:

clear()

这个方法用于清除mark和position,还有limit的位置:

public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

flip()

这个方法主要用于改变当前的Position为limit,主要是用于读取操做。

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

compact()

这个方法在读取一部分数据的时候比较经常使用。
它会把当前的Position移到0,而后position+1移到1。

public ByteBuffer compact() {
        int pos = position();
        int lim = limit();
        assert (pos <= lim);
        int rem = (pos <= lim ? lim - pos : 0);

        unsafe.copyMemory(ix(pos), ix(0), rem << 0);
        position(rem);
        limit(capacity());
        discardMark();
        return this;
    }

好比一段空间内容为:

123456789

当position的位置在2时,调用compact方法,会变成:

345678989

isDirect()

这个方法用于判断是不是直接内存。若是是返回true,若是不是返回false。

rewind()

这个方法用于重置mark标记:

public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

参考

1 Java基本数据类型
2 Java中大端与小端

相关文章
相关标签/搜索