Skip to content

Java Direct memory explain

wenhao90 edited this page Oct 26, 2016 · 1 revision

数据类型

基本类型长度

在Java中有很多的基本类型,比如:

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

不同的类型都会按照自己的位数来存储,并且可以自动进行转换提升。
byte、char、short都可以自动提升为int,如果操作数有long,就会自动提升为long,float和double也是如此。

大端小端

由于一个数据类型可能有很多个字节组成的,那么它们是如何摆放的。这个是有讲究的:

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

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

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

读写数据

在直接内存中,通过 *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
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]

常用方法

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

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;
}

原文网址:http://www.importnew.com/21998.html

Clone this wiki locally