真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JavaI/O操作及優(yōu)化詳細介紹

概要:

成都創(chuàng)新互聯(lián)是專業(yè)的濰城網(wǎng)站建設(shè)公司,濰城接單;提供成都網(wǎng)站建設(shè)、做網(wǎng)站,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行濰城網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!

流是一組有順序的,有起點和終點的字節(jié)集合,是對數(shù)據(jù)傳輸?shù)目偡Q或抽象。即數(shù)據(jù)在兩設(shè)備間的傳輸稱為流,流的本質(zhì)是數(shù)據(jù)傳輸,根據(jù)數(shù)據(jù)傳輸特性將流抽象為各種類,方便更直觀的進行數(shù)據(jù)操作。

Java I/O

I/O,即 Input/Output(輸入/輸出) 的簡稱。就 I/O 而言,概念上有 5 種模型:blocking I/O,nonblocking I/O,I/O multiplexing (select and poll),signal driven I/O (SIGIO),asynchronous I/O (the POSIX aio_functions)。不同的操作系統(tǒng)對上述模型支持不同,UNIX 支持 IO 多路復(fù)用。不同系統(tǒng)叫法不同,freebsd 里面叫 kqueue,Linux 叫 epoll。而 Windows2000 的時候就誕生了 IOCP 用以支持 asynchronous I/O。

Java 是一種跨平臺語言,為了支持異步 I/O,誕生了 NIO,Java1.4 引入的 NIO1.0 是基于 I/O 復(fù)用的,它在各個平臺上會選擇不同的復(fù)用方式。Linux 用的 epoll,BSD 上用 kqueue,Windows 上是重疊 I/O。

Java I/O 的相關(guān)方法如下所述:

同步并阻塞 (I/O 方法):服務(wù)器實現(xiàn)模式為一個連接啟動一個線程,每個線程親自處理 I/O 并且一直等待 I/O 直到完成,即客戶端有連接請求時服務(wù)器端就需要啟動一個線程進行處理。但是如果這個連接不做任何事情就會造成不必要的線程開銷,當然可以通過線程池機制改善這個缺點。I/O 的局限是它是面向流的、阻塞式的、串行的一個過程。對每一個客戶端的 Socket 連接 I/O 都需要一個線程來處理,而且在此期間,這個線程一直被占用,直到 Socket 關(guān)閉。在這期間,TCP 的連接、數(shù)據(jù)的讀取、數(shù)據(jù)的返回都是被阻塞的。也就是說這期間大量浪費了 CPU 的時間片和線程占用的內(nèi)存資源。此外,每建立一個 Socket 連接時,同時創(chuàng)建一個新線程對該 Socket 進行單獨通信 (采用阻塞的方式通信)。這種方式具有很快的響應(yīng)速度,并且控制起來也很簡單。在連接數(shù)較少的時候非常有效,但是如果對每一個連接都產(chǎn)生一個線程無疑是對系統(tǒng)資源的一種浪費,如果連接數(shù)較多將會出現(xiàn)資源不足的情況;

同步非阻塞 (NIO 方法):服務(wù)器實現(xiàn)模式為一個請求啟動一個線程,每個線程親自處理 I/O,但是另外的線程輪詢檢查是否 I/O 準備完畢,不必等待 I/O 完成,即客戶端發(fā)送的連接請求都會注冊到多路復(fù)用器上,多路復(fù)用器輪詢到連接有 I/O 請求時才啟動一個線程進行處理。NIO 則是面向緩沖區(qū),非阻塞式的,基于選擇器的,用一個線程來輪詢監(jiān)控多個數(shù)據(jù)傳輸通道,哪個通道準備好了 (即有一組可以處理的數(shù)據(jù)) 就處理哪個通道。服務(wù)器端保存一個 Socket 連接列表,然后對這個列表進行輪詢,如果發(fā)現(xiàn)某個 Socket 端口上有數(shù)據(jù)可讀時,則調(diào)用該 Socket 連接的相應(yīng)讀操作;如果發(fā)現(xiàn)某個 Socket 端口上有數(shù)據(jù)可寫時,則調(diào)用該 Socket 連接的相應(yīng)寫操作;如果某個端口的 Socket 連接已經(jīng)中斷,則調(diào)用相應(yīng)的析構(gòu)方法關(guān)閉該端口。這樣能充分利用服務(wù)器資源,效率得到大幅度提高;

異步非阻塞 (AIO 方法,JDK7 發(fā)布):服務(wù)器實現(xiàn)模式為一個有效請求啟動一個線程,客戶端的 I/O 請求都是由操作系統(tǒng)先完成了再通知服務(wù)器應(yīng)用去啟動線程進行處理,每個線程不必親自處理 I/O,而是委派操作系統(tǒng)來處理,并且也不需要等待 I/O 完成,如果完成了操作系統(tǒng)會另行通知的。該模式采用了 Linux 的 epoll 模型。

在連接數(shù)不多的情況下,傳統(tǒng) I/O 模式編寫較為容易,使用上也較為簡單。但是隨著連接數(shù)的不斷增多,傳統(tǒng) I/O 處理每個連接都需要消耗一個線程,而程序的效率,當線程數(shù)不多時是隨著線程數(shù)的增加而增加,但是到一定的數(shù)量之后,是隨著線程數(shù)的增加而減少的。所以傳統(tǒng)阻塞式 I/O 的瓶頸在于不能處理過多的連接。非阻塞式 I/O 出現(xiàn)的目的就是為了解決這個瓶頸。非阻塞 IO 處理連接的線程數(shù)和連接數(shù)沒有聯(lián)系,例如系統(tǒng)處理 10000 個連接,非阻塞 I/O 不需要啟動 10000 個線程,你可以用 1000 個,也可以用 2000 個線程來處理。因為非阻塞 IO 處理連接是異步的,當某個連接發(fā)送請求到服務(wù)器,服務(wù)器把這個連接請求當作一個請求“事件”,并把這個“事件”分配給相應(yīng)的函數(shù)處理。我們可以把這個處理函數(shù)放到線程中去執(zhí)行,執(zhí)行完就把線程歸還,這樣一個線程就可以異步的處理多個事件。而阻塞式 I/O 的線程的大部分時間都被浪費在等待請求上了。

Java NIO

Java.nio 包是 Java 在 1.4 版本之后新增加的包,專門用來提高 I/O 操作的效率。

表 1 所示是 I/O 與 NIO 之間的對比內(nèi)容。

表 1. I/O VS NIO

I/ONIO
面向流面向緩沖
阻塞 IO非阻塞 IO
選擇器

NIO 是基于塊 (Block) 的,它以塊為基本單位處理數(shù)據(jù)。在 NIO 中,最為重要的兩個組件是緩沖 Buffer 和通道 Channel。緩沖是一塊連續(xù)的內(nèi)存塊,是 NIO 讀寫數(shù)據(jù)的中轉(zhuǎn)地。通道標識緩沖數(shù)據(jù)的源頭或者目的地,它用于向緩沖讀取或者寫入數(shù)據(jù),是訪問緩沖的接口。Channel 是一個雙向通道,即可讀,也可寫。Stream 是單向的。應(yīng)用程序不能直接對 Channel 進行讀寫操作,而必須通過 Buffer 來進行,即 Channel 是通過 Buffer 來讀寫數(shù)據(jù)的。

使用 Buffer 讀寫數(shù)據(jù)一般遵循以下四個步驟:

  1. 寫入數(shù)據(jù)到 Buffer;
  2. 調(diào)用 flip() 方法;
  3. 從 Buffer 中讀取數(shù)據(jù);
  4. 調(diào)用 clear() 方法或者 compact() 方法。

當向 Buffer 寫入數(shù)據(jù)時,Buffer 會記錄下寫了多少數(shù)據(jù)。一旦要讀取數(shù)據(jù),需要通過 flip() 方法將 Buffer 從寫模式切換到讀模式。在讀模式下,可以讀取之前寫入到 Buffer 的所有數(shù)據(jù)。

一旦讀完了所有的數(shù)據(jù),就需要清空緩沖區(qū),讓它可以再次被寫入。有兩種方式能清空緩沖區(qū):調(diào)用 clear() 或 compact() 方法。clear() 方法會清空整個緩沖區(qū)。compact() 方法只會清除已經(jīng)讀過的數(shù)據(jù)。任何未讀的數(shù)據(jù)都被移到緩沖區(qū)的起始處,新寫入的數(shù)據(jù)將放到緩沖區(qū)未讀數(shù)據(jù)的后面。

Buffer 有多種類型,不同的 Buffer 提供不同的方式操作 Buffer 中的數(shù)據(jù)。

圖 1 Buffer 接口層次圖

 Java I/O 操作及優(yōu)化詳細介紹

Buffer 寫數(shù)據(jù)有兩種情況:

  1. 從 Channel 寫到 Buffer,如例子中 Channel 從文件中讀取數(shù)據(jù),寫到 Channel;
  2. 直接調(diào)用 put 方法,往里面寫數(shù)據(jù)。

從 Buffer 中讀取數(shù)據(jù)有兩種方式:

  1. 從 Buffer 讀取數(shù)據(jù)到 Channel;
  2. 使用 get() 方法從 Buffer 中讀取數(shù)據(jù)。

Buffer 的 rewin 方法將 position 設(shè)回 0,所以你可以重讀 Buffer 中的所有數(shù)據(jù)。limit 保持不變,仍然表示能從 Buffer 中讀取多少個元素(byte、char 等)。

clear() 和 compact() 方法

一旦讀完 Buffer 中的數(shù)據(jù),需要讓 Buffer 準備好再次被寫入。可以通過 clear() 或 compact() 方法來完成。

如果調(diào)用的是 clear() 方法,position 將被設(shè)回 0,limit 被設(shè)置成 capacity 的值。換句話說,Buffer 被清空了。Buffer 中的數(shù)據(jù)并未清除,只是這些標記告訴我們可以從哪里開始往 Buffer 里寫數(shù)據(jù)。

如果 Buffer 中有一些未讀的數(shù)據(jù),調(diào)用 clear() 方法,數(shù)據(jù)將“被遺忘”,意味著不再有任何標記會告訴你哪些數(shù)據(jù)被讀過,哪些還沒有。如果 Buffer 中仍有未讀的數(shù)據(jù),且后續(xù)還需要這些數(shù)據(jù),但是此時想要先寫些數(shù)據(jù),那么使用 compact() 方法。compact() 方法將所有未讀的數(shù)據(jù)拷貝到 Buffer 起始處。然后將 position 設(shè)到最后一個未讀元素正后面。limit 屬性依然像 clear() 方法一樣,設(shè)置成 capacity?,F(xiàn)在 Buffer 準備好寫數(shù)據(jù)了,但是不會覆蓋未讀的數(shù)據(jù)。

Buffer 參數(shù)

Buffer 有 3 個重要的參數(shù):位置 (position)、容量 (capacity) 和上限 (limit)。

capacity 是指 Buffer 的大小,在 Buffer 建立的時候已經(jīng)確定。

limit 當 Buffer 處于寫模式,指還可以寫入多少數(shù)據(jù);處于讀模式,指還有多少數(shù)據(jù)可以讀。

position 當 Buffer 處于寫模式,指下一個寫數(shù)據(jù)的位置;處于讀模式,當前將要讀取的數(shù)據(jù)的位置。每讀寫一個數(shù)據(jù),position+1,也就是 limit 和 position 在 Buffer 的讀/寫時的含義不一樣。當調(diào)用 Buffer 的 flip 方法,由寫模式變?yōu)樽x模式時,limit(讀)=position(寫),position(讀) =0。

散射&聚集

NIO 提供了處理結(jié)構(gòu)化數(shù)據(jù)的方法,稱之為散射 (Scattering) 和聚集 (Gathering)。散射是指將數(shù)據(jù)讀入一組 Buffer 中,而不僅僅是一個。聚集與之相反,指將數(shù)據(jù)寫入一組 Buffer 中。散射和聚集的基本使用方法和對單個 Buffer 操作時的使用方法相當類似。在散射讀取中,通道依次填充每個緩沖區(qū)。填滿一個緩沖區(qū)后,它就開始填充下一個,在某種意義上,緩沖區(qū)數(shù)組就像一個大緩沖區(qū)。在已知文件具體結(jié)構(gòu)的情況下,可以構(gòu)造若干個符合文件結(jié)構(gòu)的 Buffer,使得各個 Buffer 的大小恰好符合文件各段結(jié)構(gòu)的大小。此時,通過散射讀的方式可以一次將內(nèi)容裝配到各個對應(yīng)的 Buffer 中,從而簡化操作。如果需要創(chuàng)建指定格式的文件,只要先構(gòu)造好大小合適的 Buffer 對象,使用聚集寫的方式,便可以很快地創(chuàng)建出文件。清單 1 以 FileChannel 為例,展示如何使用散射和聚集讀寫結(jié)構(gòu)化文件。

清單 1. 使用散射和聚集讀寫結(jié)構(gòu)化文件

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOScatteringandGathering {
 public void createFiles(String TPATH){
 try {
 ByteBuffer bookBuf = ByteBuffer.wrap("java 性能優(yōu)化技巧".getBytes("utf-8"));
ByteBuffer autBuf = ByteBuffer.wrap("test".getBytes("utf-8"));
int booklen = bookBuf.limit();
int autlen = autBuf.limit();
ByteBuffer[] bufs = new ByteBuffer[]{bookBuf,autBuf};
File file = new File(TPATH);
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
FileOutputStream fos = new FileOutputStream(file);
FileChannel fc = fos.getChannel();
fc.write(bufs);
fos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

ByteBuffer b1 = ByteBuffer.allocate(booklen);
ByteBuffer b2 = ByteBuffer.allocate(autlen);
ByteBuffer[] bufs1 = new ByteBuffer[]{b1,b2};
File file1 = new File(TPATH);
try {
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
fc.read(bufs1);
String bookname = new String(bufs1[0].array(),"utf-8");
String autname = new String(bufs1[1].array(),"utf-8");
System.out.println(bookname+" "+autname);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

 }

 public static void main(String[] args){
 NIOScatteringandGathering nio = new NIOScatteringandGathering();
 nio.createFiles("C://1.TXT");
 }
}

輸出如下清單 2 所示。

清單 2. 運行結(jié)果

java 性能優(yōu)化技巧 test

清單 3 所示代碼對傳統(tǒng) I/O、基于 Byte 的 NIO、基于內(nèi)存映射的 NIO 三種方式進行了性能上的對比,使用一個有 400 萬數(shù)據(jù)的文件的讀、寫操作耗時作為評測依據(jù)。

清單 3. I/O 的三種方式對比試驗

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class NIOComparator {
 public void IOMethod(String TPATH){
 long start = System.currentTimeMillis();
 try {
DataOutputStream dos = new DataOutputStream(
 new BufferedOutputStream(new FileOutputStream(new File(TPATH))));
for(int i=0;i<4000000;i++){
dos.writeInt(i);//寫入 4000000 個整數(shù)
}
if(dos!=null){
dos.close();
}
 } catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
 } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
 }
 long end = System.currentTimeMillis();
 System.out.println(end - start);
 start = System.currentTimeMillis();
 try {
DataInputStream dis = new DataInputStream(
 new BufferedInputStream(new FileInputStream(new File(TPATH))));
for(int i=0;i<4000000;i++){
dis.readInt();
}
if(dis!=null){
dis.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

 end = System.currentTimeMillis();
 System.out.println(end - start);
 }

 public void ByteMethod(String TPATH){
 long start = System.currentTimeMillis();
 try {
FileOutputStream fout = new FileOutputStream(new File(TPATH));
FileChannel fc = fout.getChannel();//得到文件通道
ByteBuffer byteBuffer = ByteBuffer.allocate(4000000*4);//分配 Buffer
for(int i=0;i<4000000;i++){
byteBuffer.put(int2byte(i));//將整數(shù)轉(zhuǎn)為數(shù)組
}
byteBuffer.flip();//準備寫
fc.write(byteBuffer);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
 long end = System.currentTimeMillis();
 System.out.println(end - start);

 start = System.currentTimeMillis();
 FileInputStream fin;
try {
fin = new FileInputStream(new File(TPATH));
FileChannel fc = fin.getChannel();//取得文件通道
ByteBuffer byteBuffer = ByteBuffer.allocate(4000000*4);//分配 Buffer
fc.read(byteBuffer);//讀取文件數(shù)據(jù)
fc.close();
byteBuffer.flip();//準備讀取數(shù)據(jù)
while(byteBuffer.hasRemaining()){
byte2int(byteBuffer.get(),byteBuffer.get(),byteBuffer.get(),byteBuffer.get());//將 byte 轉(zhuǎn)為整數(shù)
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
 end = System.currentTimeMillis();
 System.out.println(end - start);
 }

 public void mapMethod(String TPATH){
 long start = System.currentTimeMillis();
 //將文件直接映射到內(nèi)存的方法
 try {
FileChannel fc = new RandomAccessFile(TPATH,"rw").getChannel();
IntBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE, 0, 4000000*4).asIntBuffer();
for(int i=0;i<4000000;i++){
ib.put(i);
}
if(fc!=null){
fc.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
 long end = System.currentTimeMillis();
 System.out.println(end - start);

 start = System.currentTimeMillis();
 try {
FileChannel fc = new FileInputStream(TPATH).getChannel();
MappedByteBuffer lib = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
lib.asIntBuffer();
while(lib.hasRemaining()){
lib.get();
}
if(fc!=null){
fc.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
 end = System.currentTimeMillis();
 System.out.println(end - start);

 }

 public static byte[] int2byte(int res){
 byte[] targets = new byte[4];
 targets[3] = (byte)(res & 0xff);//最低位
 targets[2] = (byte)((res>>8)&0xff);//次低位
 targets[1] = (byte)((res>>16)&0xff);//次高位
 targets[0] = (byte)((res>>>24));//最高位,無符號右移
 return targets;
 }

 public static int byte2int(byte b1,byte b2,byte b3,byte b4){
 return ((b1 & 0xff)<<24)|((b2 & 0xff)<<16)|((b3 & 0xff)<<8)|(b4 & 0xff);
 }

 public static void main(String[] args){
 NIOComparator nio = new NIOComparator();
 nio.IOMethod("c://1.txt");
 nio.ByteMethod("c://2.txt");
 nio.ByteMethod("c://3.txt");
 }
}

清單 3 運行輸出如清單 4 所示。

清單 4. 運行輸出

1139
906
296
157
234
125

除上述描述及清單 3 所示代碼以外,NIO 的 Buffer 還提供了一個可以直接訪問系統(tǒng)物理內(nèi)存的類 DirectBuffer。DirectBuffer 繼承自 ByteBuffer,但和普通的 ByteBuffer 不同。普通的 ByteBuffer 仍然在 JVM 堆上分配空間,其最大內(nèi)存受到最大堆的限制,而 DirectBuffer 直接分配在物理內(nèi)存上,并不占用堆空間。在對普通的 ByteBuffer 訪問時,系統(tǒng)總是會使用一個“內(nèi)核緩沖區(qū)”進行間接的操作。而 DirectrBuffer 所處的位置,相當于這個“內(nèi)核緩沖區(qū)”。因此,使用 DirectBuffer 是一種更加接近系統(tǒng)底層的方法,所以,它的速度比普通的 ByteBuffer 更快。DirectBuffer 相對于 ByteBuffer 而言,讀寫訪問速度快很多,但是創(chuàng)建和銷毀 DirectrBuffer 的花費卻比 ByteBuffer 高。DirectBuffer 與 ByteBuffer 相比較的代碼如清單 5 所示。

清單 5. DirectBuffer VS ByteBuffer

import java.nio.ByteBuffer;

public class DirectBuffervsByteBuffer {
 public void DirectBufferPerform(){
 long start = System.currentTimeMillis();
 ByteBuffer bb = ByteBuffer.allocateDirect(500);//分配 DirectBuffer
 for(int i=0;i<100000;i++){
 for(int j=0;j<99;j++){
 bb.putInt(j);
 }
 bb.flip();
 for(int j=0;j<99;j++){
 bb.getInt(j);
 }
 }
 bb.clear();
 long end = System.currentTimeMillis();
 System.out.println(end-start);
 start = System.currentTimeMillis();
 for(int i=0;i<20000;i++){
 ByteBuffer b = ByteBuffer.allocateDirect(10000);//創(chuàng)建 DirectBuffer
 }
 end = System.currentTimeMillis();
 System.out.println(end-start);
 }

 public void ByteBufferPerform(){
 long start = System.currentTimeMillis();
 ByteBuffer bb = ByteBuffer.allocate(500);//分配 DirectBuffer
 for(int i=0;i<100000;i++){
 for(int j=0;j<99;j++){
 bb.putInt(j);
 }
 bb.flip();
 for(int j=0;j<99;j++){
 bb.getInt(j);
 }
 }
 bb.clear();
 long end = System.currentTimeMillis();
 System.out.println(end-start);
 start = System.currentTimeMillis();
 for(int i=0;i<20000;i++){
 ByteBuffer b = ByteBuffer.allocate(10000);//創(chuàng)建 ByteBuffer
 }
 end = System.currentTimeMillis();
 System.out.println(end-start);
 }

 public static void main(String[] args){
 DirectBuffervsByteBuffer db = new DirectBuffervsByteBuffer();
 db.ByteBufferPerform();
 db.DirectBufferPerform();
 }
}

運行輸出如清單 6 所示。

清單 6. 運行輸出

920
110
531
390

由清單 6 可知,頻繁創(chuàng)建和銷毀 DirectBuffer 的代價遠遠大于在堆上分配內(nèi)存空間。使用參數(shù)-XX:MaxDirectMemorySize=200M –Xmx200M 在 VM Arguments 里面配置最大 DirectBuffer 和最大堆空間,代碼中分別請求了 200M 的空間,如果設(shè)置的堆空間過小,例如設(shè)置 1M,會拋出錯誤如清單 7 所示。

清單 7. 運行錯誤

Error occurred during initialization of VM
Too small initial heap for new size specified

DirectBuffer 的信息不會打印在 GC 里面,因為 GC 只記錄了堆空間的內(nèi)存回收??梢钥吹剑捎?ByteBuffer 在堆上分配空間,因此其 GC 數(shù)組相對非常頻繁,在需要頻繁創(chuàng)建 Buffer 的場合,由于創(chuàng)建和銷毀 DirectBuffer 的代碼比較高昂,不宜使用 DirectBuffer。但是如果能將 DirectBuffer 進行復(fù)用,可以大幅改善系統(tǒng)性能。清單 8 是一段對 DirectBuffer 進行監(jiān)控代碼。

清單 8. 對 DirectBuffer 監(jiān)控代碼

import java.lang.reflect.Field;

public class monDirectBuffer {

public static void main(String[] args){
try {
Class c = Class.forName("java.nio.Bits");//通過反射取得私有數(shù)據(jù)
Field maxMemory = c.getDeclaredField("maxMemory");
maxMemory.setAccessible(true);
Field reservedMemory = c.getDeclaredField("reservedMemory");
reservedMemory.setAccessible(true);
synchronized(c){
Long maxMemoryValue = (Long)maxMemory.get(null);
Long reservedMemoryValue = (Long)reservedMemory.get(null);
System.out.println("maxMemoryValue="+maxMemoryValue);
System.out.println("reservedMemoryValue="+reservedMemoryValue);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

運行輸出如清單 9 所示。

清單 9. 運行輸出

maxMemoryValue=67108864
reservedMemoryValue=0

由于 NIO 使用起來較為困難,所以許多公司推出了自己封裝 JDK NIO 的框架,例如 Apache 的 Mina,JBoss 的 Netty,Sun 的 Grizzly 等等,這些框架都直接封裝了傳輸層的 TCP 或 UDP 協(xié)議,其中 Netty 只是一個 NIO 框架,它不需要 Web 容器的額外支持,也就是說不限定 Web 容器。

Java AIO

AIO 相關(guān)的類和接口:

java.nio.channels.AsynchronousChannel:標記一個 Channel 支持異步 IO 操作;
java.nio.channels.AsynchronousServerSocketChannel:ServerSocket 的 AIO 版本,創(chuàng)建 TCP 服務(wù)端,綁定地址,監(jiān)聽端口等;
java.nio.channels.AsynchronousSocketChannel:面向流的異步 Socket Channel,表示一個連接;
java.nio.channels.AsynchronousChannelGroup:異步 Channel 的分組管理,目的是為了資源共享。一個 AsynchronousChannelGroup 綁定一個線程池,這個線程池執(zhí)行兩個任務(wù):處理 IO 事件和派發(fā) CompletionHandler。AsynchronousServerSocketChannel 創(chuàng)建的時候可以傳入一個 AsynchronousChannelGroup,那么通過 AsynchronousServerSocketChannel 創(chuàng)建的 AsynchronousSocketChannel 將同屬于一個組,共享資源;
java.nio.channels.CompletionHandler:異步 IO 操作結(jié)果的回調(diào)接口,用于定義在 IO 操作完成后所作的回調(diào)工作。AIO 的 API 允許兩種方式來處理異步操作的結(jié)果:返回的 Future 模式或者注冊 CompletionHandler,推薦用 CompletionHandler 的方式,這些 handler 的調(diào)用是由 AsynchronousChannelGroup 的線程池派發(fā)的。這里線程池的大小是性能的關(guān)鍵因素。

這里舉一個程序范例,簡單介紹一下 AIO 如何運作。

清單 10. 服務(wù)端程序

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;

public class SimpleServer {
public SimpleServer(int port) throws IOException { 
final AsynchronousServerSocketChannel listener = 
 AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(port));
//監(jiān)聽消息,收到后啟動 Handle 處理模塊
listener.accept(null, new CompletionHandler() {
public void completed(AsynchronousSocketChannel ch, Void att) { 
listener.accept(null, this);// 接受下一個連接 
handle(ch);// 處理當前連接 
}

@Override
public void failed(Throwable exc, Void attachment) {
// TODO Auto-generated method stub

} 

});
}

public void handle(AsynchronousSocketChannel ch) { 
ByteBuffer byteBuffer = ByteBuffer.allocate(32);//開一個 Buffer 
try { 
 ch.read(byteBuffer).get();//讀取輸入 
} catch (InterruptedException e) { 
 // TODO Auto-generated catch block 
 e.printStackTrace(); 
} catch (ExecutionException e) { 
 // TODO Auto-generated catch block 
 e.printStackTrace(); 
} 
byteBuffer.flip(); 
System.out.println(byteBuffer.get()); 
// Do something 
} 

}

清單 11. 客戶端程序

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class SimpleClientClass {
private AsynchronousSocketChannel client; 
public SimpleClientClass(String host, int port) throws IOException, 
         InterruptedException, ExecutionException { 
 this.client = AsynchronousSocketChannel.open(); 
 Future<?> future = client.connect(new InetSocketAddress(host, port)); 
 future.get(); 
} 

public void write(byte b) { 
 ByteBuffer byteBuffer = ByteBuffer.allocate(32);
 System.out.println("byteBuffer="+byteBuffer);
 byteBuffer.put(b);//向 buffer 寫入讀取到的字符 
 byteBuffer.flip();
 System.out.println("byteBuffer="+byteBuffer);
 client.write(byteBuffer); 
} 

}

清單 12.Main 函數(shù)

import java.io.IOException;
import java.util.concurrent.ExecutionException;

import org.junit.Test;

public class AIODemoTest {

@Test
public void testServer() throws IOException, InterruptedException { 
 SimpleServer server = new SimpleServer(9021); 
 Thread.sleep(10000);//由于是異步操作,所以睡眠一定時間,以免程序很快結(jié)束
} 

@Test 
public void testClient() throws IOException, InterruptedException, ExecutionException { 
SimpleClientClass client = new SimpleClientClass("localhost", 9021); 
 client.write((byte) 11); 
}

public static void main(String[] args){
AIODemoTest demoTest = new AIODemoTest();
try {
demoTest.testServer();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
demoTest.testClient();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

后續(xù)會專門出文章具體深入介紹 AIO 的源代碼、設(shè)計理念、設(shè)計模式等等。

結(jié)束語

I/O 與 NIO 一個比較重要的區(qū)別是我們使用 I/O 的時候往往會引入多線程,每個連接使用一個單獨的線程,而 NIO 則是使用單線程或者只使用少量的多線程,每個連接共用一個線程。而由于 NIO 的非阻塞需要一直輪詢,比較消耗系統(tǒng)資源,所以異步非阻塞模式 AIO 就誕生了。本文對 I/O、NIO、AIO 等三種輸入輸出操作方式進行一一介紹,力求通過簡單的描述和實例讓讀者能夠掌握基本的操作、優(yōu)化方法。

 感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!


本文題目:JavaI/O操作及優(yōu)化詳細介紹
分享路徑:http://weahome.cn/article/pgoehh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部