Netty源碼01-Java的NIO(一)

Netty源碼解析

  • Netty是目前很是流行的通訊框架,Redis,Dubbo的底層都使用了Netty
  • Netty之因此如此流行和他易用且高效是分不開的,後續我將會寫多篇文章對Netty的源碼進行分析
  • 同時要想深刻理解Netty,看懂Netty源碼和框架也須要儲備許多基礎的知識,下面就將從這些基本知識一一展開,逐步揭開Netty源碼神祕的面紗

JAVA中的IO

  • 首先要知道Netty是一個處理網絡IO的框架,Java自己就要處理IO的框架,其中就包括咱們很是熟悉的InputStream和OutputStream
  • Java的IO採用裝飾者模式

BufferedInputStream

BufferedInputStream->FilterInputStream->InputStream
這些類的核心read函數最後都是調用InputStream裏面的read函數,一個byte一個byte的讀取(int->byte),效率不高
public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

FileInputStream

FileInputStream(FileChannel(NIO))->InputStream
上面的BufferedInputStream讀取太慢,FileInputStream引入了NIO中的FileChannel(rt.jar java.nio.channels)提供了緩衝區,提升了InputStream的讀取效率

JAVA中的NIO

Java NIO有三大核心部件java

  • Buffer --- 緩衝區
  • Channel --- IO操做通道,能夠操做設備、文件、網絡套接字或程序組件
  • Selector --- 多路複用選擇器
通道(Channel)和緩衝區(Buffer)。通道表示打開到 IO 目標的鏈接。若須要使用 NIO 系統,須要獲取用於鏈接 IO 目標的通道以及用於容納數據的緩衝區。而後操做緩衝區,對數據進行處理

Buffer 緩衝區

緩衝區(Buffer):緩衝區是一個線性有限的用來存放從NIO Channel中讀取出得數據的區域
Buffer就是一個數組,用來存放一些特定的數據類型的元素,每種數據類型都有一個對應的緩衝區(除了boolean類型),他們都是Buffer類的子類,其中包括:數組

  • ByteBuffer(最經常使用)
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
allocate (int capacity) 是用來分配capacity大小空間返回緩衝區對象的函數

緩衝區(Buffer)的重要屬性

  • mark(標記):緩衝區的標記是在調用reset方法時將其position重置到的索引位置。mark並不老是被定義,mark()函數定義mark爲當前position,但當它被定義時,從不爲負,也不大於position。若是定義了mark, 而將position或limit調整爲小於mark的值時,該mark將被自動丟棄。若是未定義mark,則調用reset方法將引起InvalidMarkException
public final Buffer mark() {
      mark = position;
      return this;
}

public final Buffer reset() {
      int m = mark;
      if (m < 0)
          throw new InvalidMarkException();
      position = m;
      return this;
}
  • position(位置):是下一個被讀或寫的元素的index,緩衝區的位置從不爲負,也從不大於其極限
  • limit(臨界點):是第一個不能被讀寫的元素的index,緩衝區的界限永遠不可能爲負,也不會大於其容量
  • capacity(容量):是緩衝區包含的元素個數,緩衝區的容量永遠不會是負的,也不會改變

    mark、position、limit、capcity之間的關係:mark <= position <= limit <= capacity網絡

image.png

緩衝區的重要方法

  • get:獲取 Buffer 中的數據框架

    • get() :讀取單個字節
    • get(byte[] dst):批量讀取多個字節到 dst 中
    • get(int index):讀取指定索引位置的字節(不會移動 position)
  • put:放入數據到 Buffer 中函數

    • put(byte b):將給定單個字節寫入緩衝區的當前位置
    • put(byte[] src):將 src 中的字節寫入緩衝區的當前位置
    • put(int index, byte b):將指定字節寫入緩衝區的索引位置(不會移動 position)

image.png

  • clear:清除方法,使緩衝區爲新的通道讀取或相對put操做序列作好準備:它將limit設爲capacity,position設置爲零

image.png

  • flip:翻轉方法,使緩衝區爲新的通道寫入或相對get操做序列作好準備:它將limit設置爲當前位置,而後將位置設置爲零

image.png

  • rewind:倒帶方法,使緩衝區準備好從新讀取它已經包含的數據:它保持限制不變,並將位置設置爲零

image.png

直接緩衝區 vs 非直接緩衝區

  • 非直接內存:數據要從磁盤拷貝到內核緩衝區,從內核緩衝區拷貝到JVM緩衝區,再從JVM拷緩衝區貝到進程的堆中,堆內存是new byte[capacity]
  • 直接內存:直接內存沒有將數據複製到堆內存,只複製到JVM,直接內存就是用Unsafe類操做系統指針進行內存分配
  • 大端序傳輸:ByteBuffer緩衝區的初始字節序老是大端序

image.png

直接和間接緩衝區說明

  • 若是是直接字節緩衝區,Java虛擬機將盡最大努力直接在其上執行本機I/O操做。也就是說,它將嘗試避免在每次調用底層操做系統的本機I/O操做以前(或以後)將緩衝區的內容複製到(或從)中間緩衝區
  • 能夠經過調用此類的allocateDirect(int)工廠方法建立直接字節緩衝區。此方法返回的緩衝區一般比非直接緩衝區具備更高的分配和釋放成本。直接緩衝區的內容可能位於正常垃圾收集堆以外,所以它們對應用程序內存佔用的影響可能不明顯。所以,建議將直接緩衝區主要分配給受底層系統本機I/O操做影響的大型、長壽命緩衝區。通常來講,只有當直接緩衝區在程序性能上產生可測量的增益時,才分配直接緩衝區
  • 直接字節緩衝區也能夠由java.nio.channels.FileChannel(mmap)將文件的一個區域直接映射到內存中
  • isDirect方法能夠判斷該緩衝區是否是直接緩衝區

    未完待續:一篇文章講不完Java NIO,Channel和Selector將在下一篇文章中進行介紹性能

相關文章
相關標籤/搜索