BIO与NIO比较
文章目錄
- BIO 同步阻塞
- BIO介紹
- BIO的編程流程
- BIO實現(xiàn)通信
- 實現(xiàn)思路:
- 服務(wù)器:
- 客戶端:
- NIO 同步非阻塞
- NIO中重要組件
- channel:通道
- Buffer緩沖區(qū)
- 基本用法
- Buffer實現(xiàn)原理
- Buffer常見方法
- Buffer的分配
- selector:選擇器
- Selector概述
- selector的使用
- NIO非阻塞式網(wǎng)絡(luò)通信原理分析
- NIO實現(xiàn)
- 服務(wù)端實現(xiàn)
- 客戶端
- BIO與AIO區(qū)別
BIO 同步阻塞
服務(wù)器實現(xiàn)模式為一個連接一個線程,即客戶端有連接請求時服務(wù)器端就需要啟動
一個線程進(jìn)行處理,如果這個連接不做任何事情會造成不必要的線程開銷
BIO介紹
Java BIO 就是傳統(tǒng)的 java io 編程,其相關(guān)的類和接口在 java.ioBIO(blocking I/O) : 同步阻塞,服務(wù)器實現(xiàn)模式為一個連接一個線程,即客戶端有連接請求時服務(wù)器端就需要啟動一個線程進(jìn)行處理,如果這個連接不做任何事情會造成不必要的線程開銷,可以通過線程池機(jī)制改善(實現(xiàn)多個客戶連接服務(wù)器).
BIO的編程流程
法監(jiān)聽客戶端的Socket連接。
需要對每個客戶 建立一個線程與之通訊
BIO實現(xiàn)通信
通過BIO+線程池完成少量用戶的通信架構(gòu)
實現(xiàn)思路:
通過線程池控制解決為每個請求創(chuàng)建一個獨立線程造成線程資源耗盡的問題。
存在的問題:
但由于底層依然是采用的同步阻塞模型,因此無法從根本上解決問題。如果單個消息處理的緩慢,或者服務(wù)器線程池中的全部線程都被阻塞,那么后續(xù)socket的i/o消息都將在隊列中排隊。新的Socket請求將被拒絕,客戶端會發(fā)生大量連接超時。
服務(wù)器:
public class Server {public static void main(String[] args) {try {//注冊端口ServerSocket serverSocket = new ServerSocket(9999);//初始化一個線程對象HandlerSocketServerPool pool = new HandlerSocketServerPool(5, 20);//循環(huán)接受客戶端的請求while (true){Socket socket = serverSocket.accept();//將socket封裝成一個Runnable線程交給線程池ServerRunnableTarget runnable = new ServerRunnableTarget(socket);pool.execute(runnable);}} catch (Exception e) {e.printStackTrace();}} } //線程池類 class HandlerSocketServerPool{private ExecutorService executorService;//創(chuàng)建類的對象的時候初始化線程池對象public HandlerSocketServerPool(int maxThreadNum,int queuSize){executorService = new ThreadPoolExecutor(3, maxThreadNum, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queuSize));}/*** 提交一個方法來提交任務(wù)給線程池的任務(wù)隊列來暫時存儲,等著線程池的處理** */public void execute(Runnable target){executorService.execute(target);} } class ServerRunnableTarget implements Runnable{private Socket socket;public ServerRunnableTarget(Socket socket){this.socket=socket;}@Overridepublic void run() {//從Sacket得到一個字節(jié)輸入流try {InputStream inputStream = socket.getInputStream();//使用緩沖字符輸入流BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));String s;while ((s=bufferedReader.readLine())!=null){System.out.println(s);}} catch (Exception e) {e.printStackTrace();}} }客戶端:
public class Client {public static void main(String[] args) throws IOException {System.out.println("客戶端啟動");Socket socket = new Socket("127.0.0.1",9999);OutputStream outputStream = socket.getOutputStream();//打印流PrintStream printStream = new PrintStream(outputStream);Scanner scanner = new Scanner(System.in);while (true){System.out.println("請說:");String s = scanner.nextLine();printStream.println(s);printStream.flush();}} }NIO 同步非阻塞
NIO中重要組件
channel:通道
channel和用戶操作IO相連,但通道的使用是不能直接訪問數(shù)據(jù)的需要和緩沖區(qū)Buffer相連
讀數(shù)據(jù):將數(shù)據(jù)從channel中讀取到Buffer,從Buffer在獲取到數(shù)據(jù)
寫數(shù)據(jù):將數(shù)據(jù)線寫入Buffer,Buffer中的數(shù)據(jù)寫入到通道
channel與流stream的區(qū)別:
主要的實現(xiàn)類:
FileChannel:用于讀取、寫入、映射和操作文件的通道 DatagramChannel:通過UDP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)通道 SocketChannel:通過TCP讀寫網(wǎng)絡(luò)中的數(shù)據(jù),一般是客戶端的實現(xiàn) ServerSocketChannel:監(jiān)聽新進(jìn)來的TCP連接,對每一個連接創(chuàng)建一個SocketChannel,一般是服務(wù)端的實現(xiàn)基于SocketChannel和ServerSocketChannel實現(xiàn)C/S大致流程:
服務(wù)端
1.通過ServerSocketChannel 綁定ip地址和端口號
2.通過ServerSocketChannelImpl的accept()方法創(chuàng)建一個SocketChannel對象用戶從客戶端讀/寫數(shù)據(jù)
3.創(chuàng)建讀數(shù)據(jù)/寫數(shù)據(jù)緩沖區(qū)對象來讀取客戶端數(shù)據(jù)或向客戶端發(fā)送數(shù)據(jù)
4. 關(guān)閉SocketChannel和ServerSocketChannel
Scatter / Gather( 散射/采集 )
Buffer緩沖區(qū)
Java NIO 的 Buffer 用于和 NIO Channel(通道)交互。數(shù)據(jù)是從通道讀入緩沖區(qū),從緩沖區(qū)寫入到通道中。緩沖區(qū)本質(zhì)上是塊可以寫入數(shù)據(jù),再從中讀數(shù)據(jù)的內(nèi)存。該內(nèi)存被包裝成 NIO 的 Buffer 對象,并提供了一系列方法,方便開發(fā)者訪問該塊內(nèi)存
基本用法
使用Buffer讀寫數(shù)據(jù)一般四步走:
1、寫數(shù)據(jù)到 Buffer 2、調(diào)用buffer.flip切換為讀模式 3、從Buffer中讀取數(shù)據(jù) 4、調(diào)用clear()或者compact()清除數(shù)據(jù)當(dāng)向 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實現(xiàn)原理
Buffer就像一個數(shù)組,可以保存多個相同類型的數(shù)據(jù)。根據(jù)
數(shù)據(jù)類型不同 ,有以下 Buffer 常用子類:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
Buffer的實現(xiàn)底層是通過特定類型(byte、long…)數(shù)組來存儲數(shù)據(jù)
數(shù)組中數(shù)據(jù)的操作需要借助4個指針來操作:
容量 (capacity) :作為一個內(nèi)存塊,Buffer具有一定的固定大小,
也稱為"容量",緩沖區(qū)容量不能為負(fù),并且創(chuàng)建后不能更改。
限制 (limit):表示緩沖區(qū)中可以操作數(shù)據(jù)的大小
(limit 后數(shù)據(jù)不能進(jìn)行讀寫)。緩沖區(qū)的限制不能
為負(fù),并且不能大于其容量。 寫入模式,限制等于
buffer的容量。讀取模式下,limit等于寫入的數(shù)據(jù)量。
位置 (position):下一個要讀取或?qū)懭氲臄?shù)據(jù)的索引。
緩沖區(qū)的位置不能為 負(fù),并且不能大于其限制
標(biāo)記 (mark)與重置 (reset):標(biāo)記是一個索引,
通過 Buffer 中的 mark() 方法 指定 Buffer 中一個
特定的 position,之后可以通過調(diào)用 reset() 方法恢
復(fù)到這 個 position.
標(biāo)記、位置、限制、容量遵守以下不變式:
0 <= mark <= position <= limit <= capacity
Buffer常見方法
Buffer clear() 清空緩沖區(qū)并返回對緩沖區(qū)的引用 Buffer flip() 為 將緩沖區(qū)的界限設(shè)置為當(dāng)前位置,并將當(dāng)前位置充值為 0 int capacity() 返回 Buffer 的 capacity 大小 boolean hasRemaining() 判斷緩沖區(qū)中是否還有元素 int limit() 返回 Buffer 的界限(limit) 的位置 Buffer limit(int n) 將設(shè)置緩沖區(qū)界限為 n,并返回一個具有新 limit 的緩沖區(qū)對象 Buffer mark() 對緩沖區(qū)設(shè)置標(biāo)記 int position() 返回緩沖區(qū)的當(dāng)前位置 position Buffer position(int n) 將設(shè)置緩沖區(qū)的當(dāng)前位置為 n,并返回修改后的 Buffer 對象 int remaining() 返回 position 和 limit 之間的元素個數(shù) Buffer reset() 將位置 position 轉(zhuǎn)到以前設(shè)置的mark 所在的位置 Buffer rewind() 將位置設(shè)為為 0, 取消設(shè)置的 markBuffer的分配
要想獲得一個Buffer對象首先要進(jìn)行分配。每個Buffer類都有一個allocate方法。
直接與非直接緩沖區(qū)
ByteBufferbyte byffer可以是兩種類型,一種是基于直接內(nèi)存(也就是
非堆內(nèi)存);另一種是非直接內(nèi)存(也就是堆內(nèi)存)。對于直
接內(nèi)存來說,JVM將會在IO操作上具有更高的性能,因為它
直接作用于本地系統(tǒng)的IO操作。而非直接內(nèi)存,也就是堆內(nèi)
存中的數(shù)據(jù),如果要作IO操作,會先從本進(jìn)程內(nèi)存復(fù)制到直接
內(nèi)存,再利用本地IO處理。
從數(shù)據(jù)流的角度,非直接內(nèi)存是下面這樣的作用鏈:
本地IO–>直接內(nèi)存–>非直接內(nèi)存–>直接內(nèi)存–>本地IO
而直接內(nèi)存是:
本地IO–>直接內(nèi)存–>本地IO
很明顯,在做IO處理時,比如網(wǎng)絡(luò)發(fā)送大量數(shù)據(jù)時,直接內(nèi)
存會具有更高的效率。直接內(nèi)存使用allocateDirect創(chuàng)建,但
是它比申請普通的堆內(nèi)存需要耗費更高的性能。不過,這
部分的數(shù)據(jù)是在JVM之外的,因此它不會占用應(yīng)用的內(nèi)
存。所以呢,當(dāng)你有很大的數(shù)據(jù)要緩存,并且它的生命
周期又很長,那么就比較適合使用直接內(nèi)存。只是一般
來說,如果不是能帶來很明顯的性能提升,還是推薦直接
使用堆內(nèi)存。字節(jié)緩沖區(qū)是直接緩沖區(qū)還是非直接緩沖
區(qū)可通過調(diào)用其 isDirect() 方法來確定。
Buffer的創(chuàng)建:
ByteBuffer為例:
ByteBuffer allocate(int capacity):在堆上創(chuàng)建指定大小的緩沖
ByteBuffer allocateDirect(int capacity):在堆外空間創(chuàng)建指定大小的緩沖
ByteBuffer wrap(byte[] array):通過byte數(shù)組實例創(chuàng)建一個緩沖區(qū)
ByteBuffer wrap(byte[] array, int offset, int length) 指定byte數(shù)據(jù)中的內(nèi)容寫入到一個新的緩沖區(qū)
向Buffer寫數(shù)據(jù)
寫數(shù)據(jù)到Buffer有兩種方式:
1、從Channel寫到Buffer
2、通過Buffer的put()方法寫到Buffer里
buf.put(127);flip()方法:
flip方法將Buffer從寫模式切換到讀模式。調(diào)用flip()方法會將position設(shè)回0,并將limit設(shè)置成之前position的值。換句話說,position現(xiàn)在用于標(biāo)記讀的位置,limit表示之前寫進(jìn)了多少個byte、char等 —— 現(xiàn)在能讀取多少個byte、char等。
從Buffer讀數(shù)據(jù)
兩種方式:
1、從Buffer讀取數(shù)據(jù)到Channel。
2、使用get()方法從Buffer中讀取數(shù)據(jù)。
byte aByte = buf.get();get方法有很多版本,允許你以不同的方式從Buffer中讀取數(shù)據(jù)。例如,從指定position讀取,或者從Buffer中讀取數(shù)據(jù)到字節(jié)數(shù)組。
mark()與reset()方法
通過調(diào)用Buffer.mark()方法,可以標(biāo)記Buffer中的一個特定position。之后可以通過調(diào)用Buffer.reset()方法恢復(fù)到這個position。例如:
selector:選擇器
Selector概述
選擇器(Selector) 是 SelectableChannle 對象的多路復(fù)用器,Selector 可以同時監(jiān)控多個 SelectableChannel 的 IO 狀況,也就是說,利用 Selector可使一個單獨的線程管理多個 Channel。Selector 是非阻塞 IO 的核心
Java 的 NIO,用非阻塞的 IO 方式。可以用一個線程,處理多個的客戶端連接,就會使用到 Selector(選擇器)Selector 能夠檢測多個注冊的通道上是否有事件發(fā)生(注意:多個 Channel 以事件的方式可以注冊到同一個Selector),如果有事件發(fā)生,便獲取事件然后針對每個事件進(jìn)行相應(yīng)的處理。這樣就可以只用一個單線程去管理多個通道,也就是管理多個連接和請求。只有在 連接/通道 真正有讀寫事件發(fā)生時,才會進(jìn)行讀寫,就大大地減少了系統(tǒng)開銷,且不必為每個連接都創(chuàng)建一個線程,不用去維護(hù)多個線程避免了多線程之間的上下文切換導(dǎo)致的開銷
selector優(yōu)勢:
selector的使用
創(chuàng)建 Selector :
通過調(diào)用 Selector.open() 方法創(chuàng)建一個 Selector。
向選擇器注冊通道:
SelectableChannel.register(Selector sel, int ops)
舉例:
//1. 獲取通道 ServerSocketChannel ssChannel = ServerSocketChannel.open(); //2. 切換非阻塞模式 ssChannel.configureBlocking(false); //3. 綁定連接 ssChannel.bind(new InetSocketAddress(9898)); //4. 獲取選擇器 Selector selector = Selector.open(); //5. 將通道注冊到選擇器上, 并且指定“監(jiān)聽接收事件” ssChannel.register(selector, SelectionKey.OP_ACCEPT);當(dāng)調(diào)用 register(Selector sel, int ops) 將通道注冊選擇
器時,選擇器對通道的監(jiān)聽事件,需要通過第二個參
數(shù) ops 指定。可以監(jiān)聽的事件類型(用 可使
用 SelectionKey 的四個常量 表示):
讀 : SelectionKey.OP_READ (1)
寫 : SelectionKey.OP_WRITE (4)
連接 : SelectionKey.OP_CONNECT (8)
接收 : SelectionKey.OP_ACCEPT (16)
若注冊時不止監(jiān)聽一個事件,則可以使用“位或”操作符連接。
如:
NIO非阻塞式網(wǎng)絡(luò)通信原理分析
Selector可以實現(xiàn): 一個 I/O 線程可以并發(fā)處理 N 個客戶端連接和讀寫操作,這從根本上解決了傳統(tǒng)同步阻塞 I/O 一連接一線程模型,架構(gòu)的性能、彈性伸縮能力和可靠性都得到了極大的提升。
NIO實現(xiàn)
編寫一個 NIO 群聊系統(tǒng),實現(xiàn)客戶端與客戶端的通信需求(非阻塞)
服務(wù)器端:可以監(jiān)測用戶上線,離線,并實現(xiàn)消息轉(zhuǎn)發(fā)功能
客戶端:通過 channel 可以無阻塞發(fā)送消息給其它所有客戶端用戶,同時可以接受其它客戶端用戶通過服務(wù)端轉(zhuǎn)發(fā)來的消息
服務(wù)端實現(xiàn)
//服務(wù)端群聊系統(tǒng)實現(xiàn) public class Server {//定義一些成員屬性:選擇器 服務(wù)器通道 端口private Selector selector;private ServerSocketChannel serverSocketChannel;private static final int PORT = 9999;//定義初始化代碼邏輯public Server(){//接受選擇器try {//初始化選擇器selector = Selector.open();//初始化通道serverSocketChannel=ServerSocketChannel.open();//綁定端口serverSocketChannel.bind(new InetSocketAddress(PORT));//通道切換為非阻塞模式serverSocketChannel.configureBlocking(false);//將通道注冊到選擇器上,并且開始指定監(jiān)聽接收事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);} catch (IOException e) {e.printStackTrace();}}/*** 監(jiān)聽客戶端各種消息事件:連接、群聊、離線* */private void listen(){try {//循環(huán)判斷是否存在就緒事件while (selector.select()>0){//獲取選擇器中的所有注冊的通道中已經(jīng)就緒好的事件Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//開始遍歷這些準(zhǔn)備好的事件while (iterator.hasNext()){//提取當(dāng)前事件SelectionKey sk = iterator.next();//判斷是否為可接收事件if (sk.isAcceptable()){//獲取當(dāng)前接入的客戶端通道SocketChannel socketChannel = serverSocketChannel.accept();//通道切換為非阻塞模式socketChannel.configureBlocking(false);//將本客戶端的通道注冊到選擇器上System.out.println(socketChannel.getRemoteAddress() + " 上線 ");socketChannel.register(selector,SelectionKey.OP_READ);}//判斷是否為可讀事件if(sk.isReadable()){//讀操作和轉(zhuǎn)發(fā)給其他客戶端readClientData(sk);}iterator.remove();//處理完畢移除當(dāng)前事件}}}catch (Exception e){e.printStackTrace();}}/*** 接收當(dāng)前客戶端發(fā)送的消息,并轉(zhuǎn)發(fā)給全部客戶端通道* */private void readClientData(SelectionKey sk){SocketChannel socketChannel = null;try {//取到該讀操作的通道socketChannel = (SocketChannel) sk.channel();//創(chuàng)建緩沖區(qū)對象開始接收客戶端發(fā)送的消息ByteBuffer buffer = ByteBuffer.allocate(1024);int count = socketChannel.read(buffer);if(count>0){//設(shè)置為讀模式buffer.flip();//提取讀取到的信息String msg = new String(buffer.array(),0,count);System.out.println("接收到了客戶端信息:"+msg);//返回給其他在線客戶端消息sendMsgToAllClient(msg,socketChannel);}}catch (Exception e){//該客戶斷開連接會拋出異常,異常發(fā)出下線通知try {System.out.println(socketChannel.getRemoteAddress()+"下線了");sk.channel();//關(guān)閉通道socketChannel.close();} catch (IOException ioException) {ioException.printStackTrace();}}}//發(fā)送消息給所有在線人private void sendMsgToAllClient(String msg,SocketChannel socketChannel) throws Exception{System.out.println("服務(wù)端開始轉(zhuǎn)發(fā)消息,當(dāng)前處理的線程" + Thread.currentThread().getName());//循環(huán)給所有在線通道發(fā)送消息for (SelectionKey key:selector.keys()){Channel channel = key.channel();//不要把數(shù)據(jù)發(fā)送服務(wù)器和自己if(channel instanceof SocketChannel && socketChannel!=channel){//將消息存儲到buffer緩存ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());//將緩存寫入到通道( (SocketChannel)channel).write(buffer);}}}public static void main(String[] args) {//創(chuàng)建服務(wù)端對象Server server = new Server();//開始監(jiān)聽客戶端各種消息事件:連接、群聊、離線server.listen();} }客戶端
//客戶端群聊系統(tǒng)實現(xiàn) public class Client {private Selector selector;private static final int PDRT = 9999;private SocketChannel socketChannel;public Client(){try {//初始化選擇器selector = Selector.open();//初始化通道,并綁定通信地址與端口socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",PDRT));//通道設(shè)置為非阻塞模式socketChannel.configureBlocking(false);//將通道加載到選擇器上,并開始指定監(jiān)聽讀事件socketChannel.register(selector, SelectionKey.OP_READ);System.out.println("當(dāng)前客戶端準(zhǔn)備完成");}catch (Exception e){e.printStackTrace();}}public static void main(String[] args) {Client client = new Client();//定義一個線程負(fù)責(zé)監(jiān)聽服務(wù)端發(fā)來的線程消息new Thread(new Runnable() {@Overridepublic void run() {try {while (true){//接收讀事件client.readInfo();}} catch (IOException e) {e.printStackTrace();}}}).start();//主線程進(jìn)行發(fā)送消息Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()){String s= scanner.nextLine();//數(shù)據(jù)發(fā)送client.sendTOServer(s);}}private void sendTOServer(String s) {try {//數(shù)據(jù)經(jīng)過緩存加載到通道上socketChannel.write(ByteBuffer.wrap(s.getBytes()));} catch (IOException e) {e.printStackTrace();}}/*** ** */private void readInfo() throws IOException {//判斷選擇器是是否有就緒事件if(selector.select()>0){//循環(huán)處理這些準(zhǔn)備就緒的事件Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()){//取得當(dāng)前就緒事件SelectionKey key = iterator.next();//判斷當(dāng)前是否為讀事件if(key.isReadable()){//取得當(dāng)前通道SocketChannel selectableChannel = (SocketChannel) key.channel();//創(chuàng)建buffer緩存接收事件ByteBuffer buffer = ByteBuffer.allocate(1024);//讀取通道上的數(shù)據(jù)socketChannel.read(buffer);//輸出緩存中數(shù)據(jù)System.out.println(new String(buffer.array()).trim());}//處理完關(guān)閉該通道iterator.remove();}}} }
BIO與AIO區(qū)別
BIO與NIO一個比較重要的不同,是我們使用BIO的時候往往會引入多線程,每個連接一個單獨的線程;
而NIO則是使用單線程或者只使用少量的多線程,每個連接共用一個線程。NIO的最重要的地方是當(dāng)一個連接創(chuàng)建后,不需要對應(yīng)一個線程,這個連接會被注冊到多路復(fù)用器上面,所以所有的連接只需要一個線程就可以搞定,當(dāng)這個線程中的多路復(fù)用器進(jìn)行輪詢的時候,發(fā)現(xiàn)連接上有請求的話,才開啟一個線程進(jìn)行處理,也就是一個請求一個線程模式。
NIO比BIO最大的好處是,一個線程可以處理多個socket(channel),這樣NIO+多線程會提高網(wǎng)絡(luò)服務(wù)器的性能,最主要是大大降低線程的數(shù)量
服務(wù)器線程數(shù)量過多對系統(tǒng)有什么影響?
1.java里面創(chuàng)建進(jìn)程和線程,最終映射到本地操作系統(tǒng)上創(chuàng)建進(jìn)程和線程,拿Linux來說,fork(進(jìn)程創(chuàng)建函數(shù))和pthread_create(線程創(chuàng)建函數(shù))都是重量級的函數(shù),調(diào)用它們開銷很大
2.多線程隨著CPU的調(diào)度,會有上下文切換,如果線程過多,線程上下文切換的時間花費慢慢趨近或者大于線程本身執(zhí)行指令的時間,那么CPU就完全被浪費掉了,大大降低了系統(tǒng)的性能
3.線程的開辟伴隨著線程私有內(nèi)存的分配,如果線程數(shù)量過多,為線程運行準(zhǔn)備的內(nèi)存占去很多,真正能用來分配做業(yè)務(wù)處理的內(nèi)存大大減少,系統(tǒng)運行不可靠
4.數(shù)量過多的線程,阻塞等待網(wǎng)絡(luò)事件發(fā)生,如果一瞬間客戶請求量比較大,系統(tǒng)會瞬間喚醒很多數(shù)量的線程,造成系統(tǒng)瞬間的內(nèi)存使用率和CPU使用率居高不下,服務(wù)器系統(tǒng)不應(yīng)該總是出現(xiàn)鋸齒狀的系統(tǒng)負(fù)載,內(nèi)存使用率和CPU使用率應(yīng)該持續(xù)的保證平順運行
總結(jié)
- 上一篇: String、StringBuffer、
- 下一篇: netty使用