BIO的局限性

  Server端应该使用尽肯能少的线程来处理多个client请求。BIO下,每个client都要创建一个对应的线程来处理,线程数量越多,上限文切换造成的资源损耗越大。在BIO中使用线程池,就意味着线程池中的维护的线程数就是server端支持最多有多少个client来连接。

NIO Buffer

  一个Buffer对象是固定数量的数据的容器。其作用是一个存储器或分段运输区,在这里数据可以被存储并在之后用于检索。对于每个非布尔原始数据类型都有一个缓冲区类。尽管缓冲区作用于他们存储的原始数据类型,但缓冲区十分倾向于处理字节。缓冲区的工作与通道紧密联系。通道是I/O传输发生是通过的入口,而缓冲区是这些数据传输的来源或目标。对于离开缓冲区的传输,是将缓冲区的数据传送到通道。对于传回缓冲区的传输,是将通道的数据放置在缓冲区中。这种在协同对象之间进行的缓冲区数据传递是高校数据处理的关键。

  Buffer定义所有缓冲区类型共有的曹邹,无论是它们所包含的数据类型还是可能具有的特定行为。

  缓冲区是包在一个对象内的基本数据元素数组。Buffer类的优点是他将关于数据的数据内容和信息包含在一个单一的对象中。Buffer类以及它专有的子类定义了一个用于处理数据缓冲的API。

public abstract class Buffer{
  // mark <= position <= limit <= capacity
  private int mark = -1;
  private int position = 0';
  private int limit;
  private int capacity;

  public final int capacity();
public final int position();
public final Buffer position(int newPosition);
public final int limit();
public final Buffer limit(int newLimit);
public final Buffer mark();
public final Buffer reset();
public final Buffer clear();
public final Buffer flip();
public final Buffer rewind();
public final int remaining();
public final boolean hasRemaining();
public abstract boolean isReadOnly();

public abstract boolean hasArray();
public abstract Object array();
public abstract int arrayOffset();
public abstract boolean isDirect(); }

容量(Capacity):缓冲区能够容纳的数据元素的最大数量,可以理解为数组的长度。在缓冲区创建时被设定,并且永不能改变

上界(Limit):缓冲区的第一个不能被读或写的元素的索引。或说缓冲区中现存元素的技数

位置(Position):下一个要被读写的元素的索引。Buffer类提供了get()和put()函数来读取或存入数据,position位置会自动进行相应的更新

标记(Mark):一个备忘位置。调用mark()来设定mark = position。调用reset()设定position = mark。标记在设定前是未定义的。

public abstract class ByteBuffer{
  public static ByteBuffer allocateDirect(int capacity);
  public static ByteBuffer allocate(int capacity);
  public static ByteBuffer wrap(byte[] array);
  public static ByteBuffer wrap(byte[] array, int offset, int length);

  public abstract byte get();
  public abstract byte get(int index);
  public abstract ByteBuffer put(byte b);
  public abstract ByteBuffer put(int index, int b);
}

  新的缓冲区是由分配(allocate)或包装(wrap)操作创建的。allocate操作创建一个缓冲区对象并分配一个私有的空间来储存容量大小的数据元素。wrap操作创建一个缓冲区对象但是不分配任何空间来储存数据元素。它使用你所提供的的数组作为存储空间来储存穿冲去中的数据元素。

  存储操作时通过get和put操作进行的,get和put可以使相对的或是绝对的。相对方案是不带有索引参数的函数。当相对函数被调用时,位置在返回时前进一。若位置前进过多,相对运算就会抛出异常。绝对存取不会影响缓冲区的位置属性,但若提供的索引超出范围,也将抛出IndexOutOfBoundsException异常。

  flip()函数可以将position值重新设为0,同时将limit设置为当当前缓冲区内的最后一个元素的索引。rewind()将position设置为0单不会改变limit的值。

  clear()函数可以让缓冲区恢复到初始状态,但并不改变缓冲区中的任何数据元素。即position=0, limit=capacity,mark=-1。

  mark()方法能使缓冲区记住一个position并在之后将其返回。缓冲区的标记在mark()行数被调用之前是未定义的,值为-1,调用时mark被设为当前的position的值。reset()函数将position设为当前的mark值。若mark未定义,调用reset()将导致InvalidMarkException异常。
  compact()方法会将未读数据元素需要下移以使第一个元素索引为0。

复制缓冲区

  缓冲区的复制有两种:

    完全复制:调用duplicate()或asReadOnluBuffer()函数

    部分复制:调用slice()

  duolicate()函数创建了一个与原始缓冲区相似的新缓冲区。两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的位置,上界和标记属性。对一个缓冲区的数据元素所做的改变会反映在另一个缓冲区上。若原始的缓冲区为只读或直接缓冲区,那新的缓冲区将继承这些属性。

CharBuffer buffer = CharBuffer.allocate(8);
buffer.position(3).limit(6).mark().position(5);
CharBuffer dupBuffer = buffer.duplicate();
buffer.clear();

  asReadOnlyBuffer()函数来生成一个只读的缓冲区视图。这个新的缓冲区不允许使用put(),并且isReadOnly()会返回true。对这一只读缓冲区调用put()函数会导致ReadOnlyBufferException异常

直接缓冲区

  直接字节缓冲区通常是I/O操作最好的选择。在设计方面,它们支持JVM可用的最高效I/O机制。非直接字节缓冲区可以被传递给通道,但这样可能会导致性能损耗。通常非直接缓冲不可能成为一个本地I/O操作的目标。若向一个通道中传递一个非直接ByteBuffer对象用于写入,通道可能会在每次调用中隐含地进行下面的操作:

    1. 创建一个临时的直接ByteBuffer对象

    2. 将非直接缓冲区的内容复制到临时缓冲区中

    3. 使用临时缓冲区执行低层次I/O操作

    4. 临时缓冲区对象离开作用域,并最终成为被回收的无用数据

  直接缓冲区是I/O的最佳选择,但可能比创建非直接缓冲区要花费更高的成本。直接缓冲区使用的内存是通过调用给本地操作系统方面的代码分配的,绕过了JVM堆栈。建立和销毁直接缓冲区会明显比具有堆栈的缓冲区更加破费,具体取决于操作系统和JVM的实现。直接缓冲区的内存区域不受无用存储单元收集支配。

  直接ByteBuffer是通过调用具有所需容量的ByteBuffer.allocateDirect()函数产生的。通常用wrap()函数所创建的被包装的缓冲区总是非直接的。通过缓冲区的isDirect()方法来判断是否是直接缓冲区。

  读取数据总是需要通过内核空间传递到用户空间,而往外写数据总是要通过用户空间到内核空间。JVM堆栈属于用户空间。直接缓冲区就是内核空间。内核空间的存在java中是通过Unsafe这个类来调用的。

  

内存映射缓冲区

  映射缓冲区是带有存储的文件,通过内存映射来存取数据元素的字节缓冲区。映射缓冲区通常是直接存取内存的,只能通过FileChannel类来创建。映射缓冲区的用法和直接缓冲区类似,但MappedByteBuffer对象可以独立于文件存取形式的许多特定字符。

  MappedByteBuffer在大文件处理方面性能比较好。

优质内容筛选与推荐>>
1、Django 框架 数据库操作2
2、iOS 定义多个参数函数的写法
3、Linux常用命令大全
4、A successful Git branching model
5、创建一个MongoDB数据库再到配置成Window服务再设置用户名密码


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn