Netty常见面试题 与 答案
生活随笔
收集整理的這篇文章主要介紹了
Netty常见面试题 与 答案
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Netty基礎(chǔ)知識(shí)
什么是Netty?
- Netty 是一款用于高效開(kāi)發(fā)網(wǎng)絡(luò)應(yīng)用的 NIO 網(wǎng)絡(luò)框架,它大大簡(jiǎn)化了網(wǎng)絡(luò)應(yīng)用的開(kāi)發(fā)過(guò)程;
- 封裝了JDK底層的NIO模型,提供高度可用的API,用于快速開(kāi)發(fā)高性能服務(wù)端和客戶端;
- 精心設(shè)計(jì)的 Reactor 線程模型支持高并發(fā)海量連接;
- 自帶編解碼器解決拆包和粘包問(wèn)題,用戶只關(guān)心業(yè)務(wù)邏輯即可;
- 自帶各種協(xié)議棧,讓你處理任何一種通用協(xié)議幾乎都不用親自動(dòng)手。
Netty對(duì)比Java NIO有哪些優(yōu)勢(shì)?
- 易用性:
- 使用 JDK NIO 編程需要了解很多復(fù)雜的概念,比如 Channels、Selectors、Sockets、Buffers 等,編碼復(fù)雜程度令人發(fā)指;
- Netty 在 NIO 基礎(chǔ)上封裝了更加人性化的 API,統(tǒng)一的 API(阻塞/非阻塞) 大大降低了開(kāi)發(fā)者的上手難度;
- Netty 提供了很多開(kāi)箱即用的工具,例如常用的行解碼器、長(zhǎng)度域解碼器等,而這些在 JDK NIO 中都需要你自己實(shí)現(xiàn)。
- 穩(wěn)定性:
- Netty 更加可靠穩(wěn)定,修復(fù)和完善了 JDK NIO 較多已知問(wèn)題,例如臭名昭著的 select 空轉(zhuǎn)導(dǎo)致 CPU 消耗 100%,TCP 斷線重連,keep-alive 檢測(cè)等問(wèn)題;
- 可擴(kuò)展性:
- 一個(gè)是可定制化的線程模型,用戶可以通過(guò)啟動(dòng)的配置參數(shù)選擇 Reactor 線程模型;
- 另一個(gè)是可擴(kuò)展的事件驅(qū)動(dòng)模型,將框架層和業(yè)務(wù)層的關(guān)注點(diǎn)分離。大部分情況下,開(kāi)發(fā)者只需要關(guān)注 ChannelHandler 的業(yè)務(wù)邏輯實(shí)現(xiàn)。
- 更低的資源消耗:
- 對(duì)象池復(fù)用技術(shù)。 Netty 通過(guò)復(fù)用對(duì)象,避免頻繁創(chuàng)建和銷毀帶來(lái)的開(kāi)銷;
- 零拷貝技術(shù)。 除了操作系統(tǒng)級(jí)別的零拷貝技術(shù)外,Netty 提供了更多面向用戶態(tài)的零拷貝技術(shù),例如 Netty 在 I/O 讀寫時(shí)直接使用 DirectBuffer,從而避免了數(shù)據(jù)在堆內(nèi)存和堆外內(nèi)存之間的拷貝。
Netty與Tomcat的區(qū)別是什么?
- Netty 和 Tomcat 最大的區(qū)別在于對(duì)通信協(xié)議的支持;
- Tomcat 是一個(gè) HTTP Server,它主要解決 HTTP 協(xié)議層的傳輸,而 Netty 不僅支持 HTTP 協(xié)議,還支持 SSH、TLS/SSL 等多種應(yīng)用層的協(xié)議,而且能夠自定義應(yīng)用層協(xié)議。
- Tomcat 需要遵循 Servlet 規(guī)范(HTTP協(xié)議的請(qǐng)求/響應(yīng)模型),然而 Netty 與 Tomcat 側(cè)重點(diǎn)不同,所以不需要受到 Servlet 規(guī)范的約束,可以最大化發(fā)揮 NIO 特性;
- 如果僅僅需要一個(gè) HTTP 服務(wù)器,那么推薦使用 Tomcat。術(shù)業(yè)有專攻,Tomcat 在這方面的成熟度和穩(wěn)定性更好。但如果要做面向 TCP 的網(wǎng)絡(luò)應(yīng)用開(kāi)發(fā),那么 Netty 才是最佳選擇。
什么是 Reactor 線程模型?
- 上圖是主從Reactor多線程模型:
- MainReactor 只負(fù)責(zé)監(jiān)聽(tīng)連接建立事件;
- SubReactor 只負(fù)責(zé)監(jiān)聽(tīng)讀寫事件;
- Reactor 主線程負(fù)責(zé)通過(guò) Acceptor 對(duì)象處理 MainReactor 監(jiān)聽(tīng)到的連接建立事件,當(dāng)Acceptor 完成網(wǎng)絡(luò)連接的建立之后,MainReactor 會(huì)將建立好的連接分配給 SubReactor 進(jìn)行后續(xù)監(jiān)聽(tīng);
- 當(dāng)一個(gè)連接被分配到一個(gè) SubReactor 之上時(shí),會(huì)由 SubReactor 負(fù)責(zé)監(jiān)聽(tīng)該連接上的讀寫事件。當(dāng)有新的讀事件(OP_READ)發(fā)生時(shí),SubReactor就會(huì)調(diào)用對(duì)應(yīng)的 Handler 讀取數(shù)據(jù),然后分發(fā)給 Worker 線程池中的線程進(jìn)行處理并返回結(jié)果。待處理結(jié)束之后,Handler 會(huì)根據(jù)處理結(jié)果調(diào)用 send 將響應(yīng)返回給客戶端,當(dāng)然此時(shí)連接要有可寫事件(OP_WRITE)才能發(fā)送數(shù)據(jù)。
- Reactor的工作流程主要分為四步:
- 連接注冊(cè):Channel 建立后,注冊(cè)至 Reactor 線程中的 Selector 選擇器;
- 事件輪詢:輪詢 Selector 選擇器中已注冊(cè)的所有 Channel 的 I/O 事件;
- 事件分發(fā):為準(zhǔn)備就緒的 I/O 事件分配相應(yīng)的處理線程;
- 任務(wù)處理:Reactor 線程還負(fù)責(zé)任務(wù)隊(duì)列中的非 I/O 任務(wù),每個(gè) Worker 線程從各自維護(hù)的任務(wù)隊(duì)列中取出任務(wù)異步執(zhí)行。
Netty的架構(gòu)與核心主件
Netty的工作模型是什么?
- Netty 抽象出兩組線程池:BossGroup 專門用于接收客戶端的連接,WorkerGroup 專門用于網(wǎng)絡(luò)的讀寫;
- BossGroup 和 WorkerGroup 類型都是 NioEventLoopGroup,相當(dāng)于一個(gè)事件循環(huán)組,其中包含多個(gè)事件循環(huán)NioEventLoop;
- NioEventLoop 表示一個(gè)不斷循環(huán)的、執(zhí)行處理任務(wù)的線程,每個(gè) NioEventLoop 都有一個(gè)Selector 對(duì)象與之對(duì)應(yīng),用于監(jiān)聽(tīng)綁定在其上的連接,這些連接上的事件由 Selector 對(duì)應(yīng)的這條線程處理;
- 每個(gè) Boss NioEventLoop 會(huì)監(jiān)聽(tīng) Selector 上連接建立的 accept 事件,然后處理 accept 事件與客戶端建立網(wǎng)絡(luò)連接,生成相應(yīng)的 NioSocketChannel 對(duì)象,一個(gè) NioSocketChannel 就表示一條網(wǎng)絡(luò)連接。之后會(huì)將 NioSocketChannel 注冊(cè)到某個(gè) Worker NioEventLoop 上的 Selector 中。
- 每個(gè) Worker NioEventLoop 會(huì)監(jiān)聽(tīng)對(duì)應(yīng) Selector 上的 read/write 事件,當(dāng)監(jiān)聽(tīng)到 read/write 事件的時(shí)候,會(huì)通過(guò) Pipeline 進(jìn)行處理。一個(gè) Pipeline 與一個(gè) Channel 綁定,在 Pipeline 上可以添加多個(gè) ChannelHandler,每個(gè) ChannelHandler 中都可以包含一定的邏輯,例如編解碼等。Pipeline 在處理請(qǐng)求的時(shí)候,會(huì)按照我們指定的順序調(diào)用 ChannelHandler。
Netty的邏輯架構(gòu)是怎樣的?有哪些核心組件?
- Netty 可分為網(wǎng)絡(luò)通信層、事件調(diào)度層、服務(wù)編排層,每一層各司其職。
- 網(wǎng)絡(luò)通信層:核心組件包含BootStrap、ServerBootStrap、Channel;
- Bootstrap:客戶端啟動(dòng)器,只綁定一個(gè) EventLoopGroup;
- ServerBootStrap:服務(wù)端啟動(dòng)器,監(jiān)聽(tīng)本地端口,會(huì)綁定兩個(gè) EventLoopGroup,分別是 Boss 和 Worker;
- Channel:可以理解為是對(duì)Socket的封裝,一個(gè)Channel代表一條新連接,對(duì)于數(shù)據(jù)的讀寫都可以在這條連接上操作;:
- 事件調(diào)度層:核心組件包含EventLoopGroup、EventLoop;
- EventLoopGroup的本質(zhì)是線程池組,一個(gè) EventLoopGroup 往往包含一個(gè)或者多個(gè) EventLoop。EventLoop 用于處理 Channel 生命周期內(nèi)的所有 I/O 事件,如 accept、connect、read、write 等 I/O 事件;
- EventLoop 的本質(zhì)是線程池,每個(gè) EventLoop 負(fù)責(zé)處理多個(gè) Channel;
- 服務(wù)編排層:核心組件包括 ChannelPipeline、ChannelHandler、ChannelHandlerContext;
- ChannelPipeline:負(fù)責(zé)組裝各種 ChannelHandler,內(nèi)部通過(guò)雙向鏈表將不同的 ChannelHandler 鏈接在一起。
- ChannelHandler:字面含義上可以反映出,ChannelHandler是用來(lái)操作Channel的,ChannelPipeline與ChannelHandler組成了一種責(zé)任鏈模式,一般ChannelHandler是直接面對(duì)開(kāi)發(fā)者的,數(shù)據(jù)的編碼解碼等都是由ChannelHandler完成;
- ChannelHandlerContext:用于保存 ChannelHandler 上下文,通過(guò) ChannelHandlerContext 我們可以知道 ChannelPipeline 和 ChannelHandler 的關(guān)聯(lián)關(guān)系。
Netty組件的工作流程是怎樣的?
- 服務(wù)端啟動(dòng)初始化時(shí)有 Boss EventLoopGroup 和 Worker EventLoopGroup 兩個(gè)組件,其中 Boss 負(fù)責(zé)監(jiān)聽(tīng)網(wǎng)絡(luò)連接事件。當(dāng)有新的網(wǎng)絡(luò)連接事件到達(dá)時(shí),則將 Channel 注冊(cè)到 Worker EventLoopGroup;
- Worker EventLoopGroup 會(huì)被分配一個(gè) EventLoop 負(fù)責(zé)處理該 Channel 的讀寫事件。每個(gè) EventLoop 都是單線程的,通過(guò) Selector 進(jìn)行事件循環(huán);
- 當(dāng)客戶端發(fā)起 I/O 讀寫事件時(shí),服務(wù)端 EventLoop 會(huì)進(jìn)行數(shù)據(jù)的讀取,然后通過(guò) Pipeline 觸發(fā)各種監(jiān)聽(tīng)器進(jìn)行數(shù)據(jù)的加工處理;
- 客戶端數(shù)據(jù)會(huì)被傳遞到 ChannelPipeline 的第一個(gè) ChannelInboundHandler 中,數(shù)據(jù)處理完成后,將加工完成的數(shù)據(jù)傳遞給下一個(gè) ChannelInboundHandler;
- 當(dāng)數(shù)據(jù)寫回客戶端時(shí),會(huì)將處理結(jié)果在 ChannelPipeline 的 ChannelOutboundHandler 中傳播,最后到達(dá)客戶端。
EventLoop 是一種什么模型?
- EventLoop 這個(gè)概念其實(shí)并不是 Netty 獨(dú)有的,它是一種事件等待和處理的程序模型,可以解決多線程資源消耗高的問(wèn)題。
- 每當(dāng)事件發(fā)生時(shí),應(yīng)用程序都會(huì)將產(chǎn)生的事件放入事件隊(duì)列當(dāng)中,然后 EventLoop 會(huì)輪詢從隊(duì)列中取出事件執(zhí)行或者將事件分發(fā)給相應(yīng)的事件監(jiān)聽(tīng)者執(zhí)行。事件執(zhí)行的方式通常分為立即執(zhí)行、延后執(zhí)行、定期執(zhí)行幾種。
- 在Netty 中EventLoop的實(shí)現(xiàn)類叫做NioEventLoop,是 Reactor 線程模型的事件處理引擎。
Netty 的無(wú)鎖化設(shè)計(jì)體現(xiàn)在哪?
- 當(dāng)accept事件觸發(fā)時(shí),事件會(huì)被注冊(cè)到WorkerEventLoopGroup 中的一個(gè) NioEventLoop 上;
- 由于每個(gè)請(qǐng)求的Channel都只與一個(gè)NioEventLoop綁定,所以說(shuō) Channel 生命周期的所有事件處理都是線程獨(dú)立的,不同的 NioEventLoop 線程之間不會(huì)發(fā)生任何交集;
- NioEventLoop 完成數(shù)據(jù)讀取后,會(huì)調(diào)用綁定的 ChannelPipeline 進(jìn)行事件傳播,數(shù)據(jù)在傳播過(guò)程中由具體的ChannelHandler處理,整個(gè)過(guò)程是串行化執(zhí)行,沒(méi)有線程安全問(wèn)題。
Netty 是如何解決 JDK epoll 空輪詢的 Bug 的?
- Selector每次執(zhí)行 select 操作之前記錄當(dāng)前時(shí)間 currentTimeNanos;
- 然后計(jì)算本次select的截止時(shí)間deadline;
- 根據(jù)當(dāng)前時(shí)間與截止時(shí)間比較,如果超時(shí),結(jié)束本次select輪詢操作;
- 如果沒(méi)有超時(shí),且任務(wù)隊(duì)列中出現(xiàn)任務(wù)需要處理,結(jié)束select輪詢開(kāi)始處理任務(wù);
- 如果沒(méi)有超時(shí),且任務(wù)隊(duì)列沒(méi)有任務(wù), 調(diào)用NIO底層的select方法進(jìn)行阻塞,會(huì)一直阻塞到截止時(shí)間,同時(shí)記錄輪詢次數(shù)。阻塞可以被外部任務(wù)喚醒;
- 阻塞結(jié)束后,如果阻塞時(shí)間小于截止時(shí)間,說(shuō)明阻塞被提前喚醒,如果喚醒沒(méi)有任務(wù),說(shuō)明可能觸發(fā)了空輪詢的Bug;
- Netty會(huì)對(duì)空輪詢次數(shù)進(jìn)行統(tǒng)計(jì),當(dāng)次數(shù)達(dá)到一定閾值(512)時(shí),重建Selector,將老的Selector上的Channel注冊(cè)到新Selector上。
ChannelPipeline中的Inbound事件與Outbound事件的區(qū)別是什么?
- ChannelPipeline是一個(gè)雙向鏈表結(jié)構(gòu),頭尾分別維護(hù)了Head節(jié)點(diǎn)與Tail節(jié)點(diǎn),用戶自定義的ChannelHandler會(huì)被插入到Head與Tail之間;
- Inbound事件與Outbound事件是ChannelPipeline最主要的兩種事件傳播方式,兩者的主要區(qū)別是事件類型與傳播順序;
- 傳播順序:
- Inbound事件的傳播順序?yàn)?#xff1a;Head -> h1 -> h2 -> h3 -> Tail;
- Outbount事件的傳播順序正好相反: Tail -> h3 -> h2 -> h1 -> Head;
- 事件類型:
- Inbound事件一般指應(yīng)用程序被動(dòng)接收的事件,由外部觸發(fā),例如接收了新的I/O事件,Tail節(jié)點(diǎn)會(huì)做一些收尾工作,如資源釋放等;
- Outbount一般由應(yīng)用程序主動(dòng)觸發(fā),例如應(yīng)用程序從socket讀取或?qū)懭霐?shù)據(jù),head節(jié)點(diǎn)會(huì)執(zhí)行操作系統(tǒng)底層的api完成具體的動(dòng)作。
ChannelPipeline中異常傳播的順序是什么?
- 異常傳播順序與ChannelHandler的注冊(cè)順序一致,與Inbound和Outbound無(wú)關(guān)。
Netty的編解碼
什么叫做拆包與粘包?
- TCP 傳輸協(xié)議是面向流的,沒(méi)有數(shù)據(jù)包界限。客戶端向服務(wù)端發(fā)送數(shù)據(jù)時(shí),可能將一個(gè)完整的報(bào)文拆分成多個(gè)小報(bào)文進(jìn)行發(fā)送,也可能將多個(gè)報(bào)文合并成一個(gè)大的報(bào)文進(jìn)行發(fā)送。因此就有了拆包和粘包。
Netty如何解決半包與粘包問(wèn)題的?
- FixedLengthFrameDecoder 用來(lái)解決固定大小數(shù)據(jù)包的粘包問(wèn)題;
- LineBasedFrameDecoder 適合對(duì)文本進(jìn)行按行分包;
- DelimiterBasedFrameDecoder 適合按特殊字符作為分包標(biāo)記的場(chǎng)景;
- LengthFieldBasedFrameDecoder 可以支持復(fù)雜的自定義協(xié)議分包等等。
內(nèi)存管理與ByteBuf
JVM堆內(nèi)內(nèi)存與堆外內(nèi)存的區(qū)別是什么?
- 堆內(nèi)內(nèi)存由 JVM GC 自動(dòng)回收內(nèi)存,降低了 Java 用戶的使用難度;
- 堆外內(nèi)存不受 JVM 管理,使用后需要手動(dòng)釋放,如果使用不當(dāng)容易造成內(nèi)存泄漏,且排查問(wèn)題會(huì)比較困難;
- 當(dāng)進(jìn)行網(wǎng)絡(luò) I/O 操作、文件讀寫時(shí),堆內(nèi)內(nèi)存都需要轉(zhuǎn)換為堆外內(nèi)存,然后再與底層設(shè)備進(jìn)行交互,所以直接使用堆外內(nèi)存可以減少一次內(nèi)存拷貝;
- 堆外內(nèi)存可以實(shí)現(xiàn)進(jìn)程之間、JVM 多實(shí)例之間的數(shù)據(jù)共享。
Netty的零拷貝指的是什么?
- DirectByteBuffer:
- Netty提供的DirectByteBuffer,直接將數(shù)據(jù)分配到堆外內(nèi)存中,避免在 Socket 讀寫時(shí)緩沖數(shù)據(jù)在堆外與堆內(nèi)進(jìn)行頻繁復(fù)制;
- CompositeByteBuf:
- 對(duì)于傳統(tǒng)的ByteBuffer,如果需要將兩個(gè)ByteBuffer中的數(shù)據(jù)組合到一起,需要首先創(chuàng)建一個(gè)size=size1+size2大小的新的數(shù)組,然后將兩個(gè)數(shù)組中的數(shù)據(jù)拷貝到新的數(shù)組中;
- Netty利用CompositeByteBuf可以避免這種內(nèi)存拷貝,因?yàn)镃ompositeByteBuf并沒(méi)有真正將多個(gè)Buffer組合起來(lái),而是保存了它們的引用,從而實(shí)現(xiàn)了零拷貝;
- FileRegion:
- Netty 使用 FileRegion 實(shí)現(xiàn)文件傳輸,FileRegion 底層封裝了 FileChannel#transferTo() 方法,可以將文件緩沖區(qū)的數(shù)據(jù)直接傳輸?shù)侥繕?biāo) Channel,避免內(nèi)核緩沖區(qū)和用戶態(tài)緩沖區(qū)之間的數(shù)據(jù)拷貝,這屬于操作系統(tǒng)級(jí)別的零拷貝。
Netty如何回收堆外內(nèi)存?
- 首先Netty是通過(guò)DirectByteBuffer對(duì)象分配堆外內(nèi)存的,在堆內(nèi)存放的 DirectByteBuffer 對(duì)象并不大,僅僅包含堆外內(nèi)存的地址、大小等屬性,同時(shí)還會(huì)創(chuàng)建對(duì)應(yīng)的 Cleaner 對(duì)象,這個(gè)Cleaner是專門用來(lái)回收堆外內(nèi)存的;
- Cleaner是JAVA四種引用類型中PhantomReference(虛引用)的子類,PhantomReference不能單獨(dú)使用,必須與ReferenceQueue聯(lián)合使用,ReferenceQueue 用于保存需要回收的 Cleaner 對(duì)象;
- 當(dāng)JVM發(fā)生 GC 時(shí),DirectByteBuffer 對(duì)象被回收,此時(shí) Cleaner 對(duì)象不再有任何引用關(guān)系,然后被添加到ReferenceQueue中,并執(zhí)行clean方法,clean() 方法主要做兩件事情:
- 將 Cleaner 對(duì)象從 Cleaner 鏈表中移除;
- 調(diào)用 unsafe.freeMemory 方法清理堆外內(nèi)存;
- 總體來(lái)說(shuō),Netty是通過(guò)虛引用的特性將堆外內(nèi)存對(duì)象與堆內(nèi)內(nèi)存對(duì)象聯(lián)系起來(lái),然后在JVM GC時(shí)進(jìn)行同步回收;
JDK NIO 的 ByteBuffer 有什么缺陷?
- ByteBuffer 分配的長(zhǎng)度是固定的,無(wú)法動(dòng)態(tài)擴(kuò)縮容,所以很難控制需要分配多大的容量;
- ByteBuffer 只能通過(guò) position 獲取當(dāng)前可操作的位置,因?yàn)樽x寫共用的 position 指針,所以需要頻繁調(diào)用 flip、rewind 方法切換讀寫狀態(tài),對(duì)使用者不友好,容易出錯(cuò)。
Netty 的 ByteBuf 有什么優(yōu)勢(shì)?
- 容量可以按需動(dòng)態(tài)擴(kuò)展,類似于 StringBuffer;
- 讀寫采用了不同的指針,讀寫模式可以隨意切換,不需要調(diào)用 flip 方法;
- 通過(guò)內(nèi)置的復(fù)合緩沖類型可以實(shí)現(xiàn)零拷貝;
- 支持引用計(jì)數(shù);
- 支持緩存池。
Netty 的 ByteBuf有哪些分類?
- Pooled與Unpooled(池化與非池化)
- Pooled(池化)方式每次分配內(nèi)存時(shí)都會(huì)從系統(tǒng)預(yù)先分配好的一段內(nèi)存中來(lái)取;
- Unpooled(非池化)方式每次分配內(nèi)存時(shí)都會(huì)調(diào)用系統(tǒng)API向操作系統(tǒng)申請(qǐng)內(nèi)存創(chuàng)建ByteBuf;
- Unsafe與非Unsafe
- Unsafe會(huì)先計(jì)算數(shù)據(jù)的內(nèi)存地址+偏移量,通過(guò)unsafe對(duì)象的native API來(lái)操作數(shù)據(jù);
- 非Unsafe不依賴JDK的unsafe對(duì)象,它是通過(guò)數(shù)組+下標(biāo)方式來(lái)獲取數(shù)據(jù),或者是通過(guò)JDK 底層的ByteBuffer API進(jìn)行讀寫,一般而言u(píng)nsafe方式效率更高一些;
- Heap與Direct
- Heap代表堆上內(nèi)存分配,會(huì)被JVM GC管理;
- Direct代表堆外內(nèi)存分配,調(diào)用JDK底層API進(jìn)行分配系統(tǒng)內(nèi)存,效率更高,但不受GC直接控制,需要手動(dòng)釋放內(nèi)存。
Netty 的內(nèi)存規(guī)格是怎樣的?
- Chunk:Netty中所有內(nèi)存都是以Chunk為單位分配的,一個(gè)Chunk有16M,例如當(dāng)前需要1M內(nèi)存,那么就需要向系統(tǒng)申請(qǐng)一個(gè)Chunk單位的內(nèi)存,然后再?gòu)倪@個(gè)Chunk中進(jìn)一步劃分;
- Page:Chunk的劃分單位為Page,一個(gè)Page有8K,那么一個(gè)Chunk就可以劃分出2048個(gè)Page;
- SubPage:有時(shí)候我們需要的內(nèi)存遠(yuǎn)達(dá)不到一個(gè)Page的大小,那么Netty根據(jù)實(shí)際需要對(duì)Page進(jìn)一步劃分成SubPage。
Netty 的內(nèi)存池是如何設(shè)計(jì)的?
- Netty的內(nèi)存池分四種內(nèi)存規(guī)格管理內(nèi)存,分別為 Tiny、Small、Normal、Huge,PoolChunk 負(fù)責(zé)管理 8K 以上的內(nèi)存分配,PoolSubpage 用于管理 8K 以下的內(nèi)存分配。當(dāng)申請(qǐng)內(nèi)存大于 16M 時(shí),不會(huì)經(jīng)過(guò)內(nèi)存池,直接分配。
- 設(shè)計(jì)了本地線程緩存機(jī)制 PoolThreadCache,用于提升內(nèi)存分配時(shí)的并發(fā)性能。用于申請(qǐng) Tiny、Small、Normal 三種類型的內(nèi)存時(shí),會(huì)優(yōu)先嘗試從 PoolThreadCache 中分配;
- PoolChunk 使用伙伴算法管理 Page,以二叉樹(shù)的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),是整個(gè)內(nèi)存池分配的核心所在;
- 每調(diào)用 PoolThreadCache 的 allocate() 方法到一定次數(shù),會(huì)觸發(fā)檢查 PoolThreadCache 中緩存的使用頻率,使用頻率較低的內(nèi)存塊會(huì)被釋放;
- 線程退出時(shí),Netty 會(huì)回收該線程對(duì)應(yīng)的所有內(nèi)存。
Netty 的對(duì)象池是如何設(shè)計(jì)的?
- Netty 為了避免多線程競(jìng)爭(zhēng)問(wèn)題,每個(gè)線程都會(huì)持有各自的 Recycler 對(duì)象池,內(nèi)部通過(guò) FastThreadLocal 來(lái)實(shí)現(xiàn)每個(gè)線程的私有化;
- Recycler 有兩個(gè)重要的組成部分:Stack 和 WeakOrderQueue;
- 從 Recycler 獲取對(duì)象時(shí),優(yōu)先從 Stack 中查找,如果 Stack 沒(méi)有可用對(duì)象,會(huì)嘗試從 WeakOrderQueue 遷移部分對(duì)象到 Stack 中;
- Recycler 回收對(duì)象時(shí),分為同線程對(duì)象回收和異線程對(duì)象回收兩種情況,同線程回收直接向 Stack 中添加對(duì)象,異線程回收向 WeakOrderQueue 中的 Link 添加對(duì)象;
- 對(duì)象回收都會(huì)控制回收速率,每 8 個(gè)對(duì)象會(huì)回收一個(gè),其他的全部丟棄。
總結(jié)
以上是生活随笔為你收集整理的Netty常见面试题 与 答案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: opencv精要(3)-win下code
- 下一篇: Java Stream Collecto