Buffer类族

从分析VasDolly的源码和android自身的签名、校验源码,经常可以看到ByteBuffer的身影。它是什么东西呢?有什么优势?下面具体分析下整个类以及相关的类。

Buffer类族是为基本数据类型(除boolean类型外)的数据提供一个线性的有限长度的特定序列的结构

Buffer类不是线程安全的,应该避免多线程读写同一个Buffer。

Buffer.java

主要的属性

  • capacity:由于是有限的数据结构,这是它的容量
  • limit:真正读写的上线,一般情况下等于capacity
  • position:当前读写的位置
  • mark:为某次读过的位置做标记,从而可以回退到该位置
0 <= mark <= position <= limit <= capacity

具体操作

具体的操作都是针对上面的属性值的改变。

equals()

当满足下列条件时,表示两个Buffer相等:

  • 相同的数据类型
  • remaining的个数相等
  • remaining中的各元素相等
public boolean equals(Object ob) {
        if (this == ob)
            return true;
        if (!(ob instanceof ByteBuffer))
            return false;
        ByteBuffer that = (ByteBuffer)ob;
        if (this.remaining() != that.remaining())
            return false;
        int p = this.position();
        for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--)
            if (!equals(this.get(i), that.get(j)))
                return false;
        return true;
    }

!!!为啥是比较剩余的是否相等???

compareTo()

比较两个buffer大小关系。

public int compareTo(ByteBuffer that) {
        int n = this.position() + Math.min(this.remaining(), that.remaining());
        for (int i = this.position(), j = that.position(); i < n; i++, j++) {
            int cmp = compare(this.get(i), that.get(j));
            if (cmp != 0)
                return cmp;
        }
        return this.remaining() - that.remaining();
    }
  • 从position开始,到limit,比较不相等的元素
  • 如果等相等,则比较remaining的个数

PS:

不带参数的get() 和 put()都是会将position加1的

Buffer的子类

  • ByteBuffer:处理字节数据类型
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer
  • MappedByteBuffer

MappedByteBuffer是以虚拟内存作为操作对象的直接字节缓存区。对象的创建是通过FileChannel.map创建。

它有3个重要的方法:

  1. fore():缓冲区内容的修改强行写入文件
  2. load():将缓冲区的内容载入内存,并返回该缓冲区的引用
  3. isLoaded():如果缓冲区的内容在物理内存中,则返回真,否则返回假

FileChannel.map方法是将文件映射到虚拟内存中,并返回逻辑地址。

直接缓存区和间接缓存区

间接缓存区

该缓存区是在JVM的内存中创建,每次的IO,JVM都会将缓存区的内容复制到中间缓存区中,或者从中间缓存区复制内容到建立的缓存区。

缓存区都是驻留在JVM中,因此销毁容易,但是占用JVM的内存开销。

  1. 创建一个临时的直接ByteBuffer对象。
  2. 将非直接缓冲区的内容复制到临时缓冲中。
  3. 使用临时缓冲区执行低层次I/O操作。
  4. 临时缓冲区对象离开作用域,并最终成为被回收的无用数据。

直接缓存区

直接缓冲区,Java虚拟机直接执行native I/O操作,避免在操作系统的native I/O操作时还要复制内容到一个中间缓冲区。

它使用Native函数库直接分配堆外内存,然后通过一个存储在JAVA堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。

可以通过allocateDirect工厂方法直接创建直接缓冲区, 内部会创建DirectByteBuffer对象, 通过unsafe.allocateMemory分配内存。 相对而言, 这个方法返回的缓冲区要比非直接缓冲区多少有点更高的分配/销毁的花费 (时间和空间)。 直接缓冲区在垃圾回收堆的外部, 所以建议主要用于大的长时间活动的缓冲区,确实能提高性能的环境中。

虽然直接缓冲区使JVM可以进行高效的I/O操作,但它使用的内存是操作系统分配的,绕过了JVM堆栈,建立和销毁比堆栈上的缓冲区要更大的开销。

Buffer的操作

创建

CharBuffer cb = CharBuffer.allocate(1024);
ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024);

包装

int[] bytes = new int[1024];
IntBuffer ib = IntBuffer.wrap(bytes);

内存映射

FileChannel fc = new RandomAccessFile("test.data", "rw").getChannel();   
MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length);

results matching ""

    No results matching ""