tomcat和Netty怎么解決內(nèi)存泄漏問題

這篇文章主要講解了“tomcat和Netty怎么解決內(nèi)存泄漏問題”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“tomcat和Netty怎么解決內(nèi)存泄漏問題”吧!

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

排查過程如下:

第一步,要日志

分部給到的異常日志大概是這樣(鑒于公司規(guī)定禁止截圖禁止拍照禁止外傳任何信息,下面是我網(wǎng)上找到一張類似的報錯):

LEAK: ByteBuf.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.  Recent access records:   #1:    io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:273)   io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253)   io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)   io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)     io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)     io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)     io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)     io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)     io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)     io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)     io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:646)     io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:581)     io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:498)     io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:460)     io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)     java.lang.Thread.run(Thread.java:748)

這一看,不得了了,ByteBuf沒有釋放,導(dǎo)致內(nèi)存泄漏了。

第二步,看內(nèi)存指標(biāo)

既然知道了是內(nèi)存泄漏,趕緊讓運(yùn)維看下內(nèi)存使用情況,特別是堆外內(nèi)存使用情況(因?yàn)橛昧薔etty),根據(jù)運(yùn)維反饋,堆內(nèi)內(nèi)存使用正常,堆外內(nèi)存居高不下。

OK,到這里已經(jīng)可以很明確地斷言:堆外內(nèi)存泄漏了。

此時,分兩步走,一步是把gateway換成zuul壓測觀察,一步是內(nèi)存泄漏問題排查。

第三步,要代碼

讓分部這個項目的負(fù)責(zé)人把代碼給到我,我打開一看,傻眼了,就一個簡單的Spring Cloud Gateway項目,里面還包含了兩個類,一個是AuthFilter用來做權(quán)限校驗(yàn)的,一個是XssFilter用來防攻擊的。

第四步,初步懷疑

快速掃一下各個類的代碼,在XssFilter里面看到了跟ByteBuf相關(guān)的代碼,但是,沒有明顯地ByteBuf沒有釋放的信息,很簡單,先把這個類屏蔽掉,看看還有沒有內(nèi)存泄漏。

但是,怎么檢測有沒有內(nèi)存泄漏呢?總不能把這個類刪掉,在生產(chǎn)上跑吧。

第五步,參數(shù)及監(jiān)控改造

其實(shí),很簡單,看過Netty源碼的同學(xué),應(yīng)該比較清楚,Netty默認(rèn)使用的是池化的直接內(nèi)存實(shí)現(xiàn)的ByteBuf,即PooledDirectByteBuf,所以,為了調(diào)試,首先,要把池化這個功能關(guān)閉。

  • 直接內(nèi)存,即堆外內(nèi)存。

為什么要關(guān)閉池化功能?

因?yàn)槌鼗菍?nèi)存的一種緩存,它一次分配16M內(nèi)存且不會立即釋放,開啟池化后不便觀察,除非慢慢調(diào)試。

那么,怎么關(guān)閉池化功能呢?

在Netty中,所有的ByteBuf都是通過一個叫作ByteBufAllocator來創(chuàng)建的,在接口ByteBufAllocator中有一個默認(rèn)的分配器,找到這個默認(rèn)的分配器,再找到它創(chuàng)建的地方,就可以看到相關(guān)的代碼了。

public interface ByteBufAllocator {      ByteBufAllocator DEFAULT = ByteBufUtil.DEFAULT_ALLOCATOR; } public final class ByteBufUtil {      static final ByteBufAllocator DEFAULT_ALLOCATOR;      static {         // 本文來源于工縱耗彤哥讀源碼         String allocType = SystemPropertyUtil.get(                 "io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");         allocType = allocType.toLowerCase(Locale.US).trim();          ByteBufAllocator alloc;         if ("unpooled".equals(allocType)) {             alloc = UnpooledByteBufAllocator.DEFAULT;             logger.debug("-Dio.netty.allocator.type: {}", allocType);         } else if ("pooled".equals(allocType)) {             alloc = PooledByteBufAllocator.DEFAULT;             logger.debug("-Dio.netty.allocator.type: {}", allocType);         } else {             alloc = PooledByteBufAllocator.DEFAULT;             logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);         }          DEFAULT_ALLOCATOR = alloc;     }  }

可以看到,是通過io.netty.allocator.type這個參數(shù)控制的。

OK,在JVM啟動參數(shù)中添加上這個參數(shù),并把它賦值為unpooled。

tomcat和Netty怎么解決內(nèi)存泄漏問題

關(guān)閉了池化功能之后,還要能夠?qū)崟r地觀測到內(nèi)存是不是真的有泄漏,這要怎么做呢?

其實(shí),這個也很簡單,Netty的PlatformDependent這個類會統(tǒng)計所有直接內(nèi)存的使用。

最近一直在研究Netty的源碼,所以,我對Netty的各種細(xì)節(jié)了解地很清楚,本文來源于工縱耗彤哥讀源碼,最近還在準(zhǔn)備,等后面弄完了,開始Netty專欄的創(chuàng)作。

所以,我們只需要寫一個定時器,定時地把這個統(tǒng)計信息打印出來就可以了,這里,我就直接給出代碼了:

@Component public class Metrics {      @PostConstruct     public void init() {         ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();         scheduledExecutorService.scheduleAtFixedRate(()->{             System.out.println("used direct memory: " + PlatformDependent.usedDirectMemory());         }, 1, 1, TimeUnit.SECONDS);     } }

把它扔到跟啟動類同級或下級的目錄就可以了。

到這里,池化及監(jiān)控都弄好了,下面就是調(diào)試了。

第六步,初步調(diào)試

直接運(yùn)行啟動類,觀察日志。

used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0

一開始,直接內(nèi)存都很正常,一直是0。

隨便發(fā)送一個請求,報404了,而且觀察直接內(nèi)存并沒有變化,還是0,說明,隨便模擬一個請求還不行,這直接被spring給攔截了,還沒到Netty。

第七步,修改配置

隨便一個請求不行, 那只能模擬正常的請求轉(zhuǎn)發(fā)了,我快速啟動了一個SpringBoot項目,并在里面定義了一個請求,修改gateway的配置,讓它可以轉(zhuǎn)發(fā)過去:

spring:   cloud:     gateway:       routes:       - id: test         uri: http://localhost:8899/test         predicates:         - Path=/test

第八步,再次調(diào)試

修改完配置,同時啟動兩個項目,一個gateway,一個springboot,請求發(fā)送,觀察直接內(nèi)存的使用情況:

used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 1031 used direct memory: 1031 used direct memory: 1031

果然,內(nèi)存沒有釋放。

第九步,刪除XssFilter

為了驗(yàn)證前面初步懷疑的XssFilter,把它刪掉,再次啟動項目,發(fā)送請求,觀察直接內(nèi)存的使用。

used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 1031 used direct memory: 1031 used direct memory: 1031

問題依然存在,而且,還是跟前面泄漏的一樣大小。

這個是這樣的,Netty是靠猜(guess)來決定每次內(nèi)存分配的大小的,這個猜的初始值是1024。

@Override public ByteBuf allocate(ByteBufAllocator alloc) {     return alloc.ioBuffer(guess()); }

是不是沒想到Netty還有這么可愛的一面^^,咳咳,跑題了,強(qiáng)行拉回!

然后,這里還有個7B存儲的是換行符回車符啥的,這7B是不會釋放的,加到一起就是1031。

private static final byte[] ZERO_CRLF_CRLF = { '0', CR, LF, CR, LF }; // 2B private static final ByteBuf CRLF_BUF = unreleasableBuffer(directBuffer(2).writeByte(CR).writeByte(LF)); // 5B private static final ByteBuf ZERO_CRLF_CRLF_BUF = unreleasableBuffer(directBuffer(ZERO_CRLF_CRLF.length)         .writeBytes(ZERO_CRLF_CRLF));

嗯,有點(diǎn)意思,既然不是XssFilter的問題,那么,會不會是AuthFilter的問題呢?

第十步,干掉AuthFilter

說干就干,干掉AuthFiler,重啟項目,發(fā)送請求,觀察直接內(nèi)存:

used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 1031 used direct memory: 1031 used direct memory: 1031

問題還是存在,還是熟悉的內(nèi)存大小。

此時,我的思路已經(jīng)不順暢了,下面是跑偏之路。

第十一步,思考

在把XssFilter和AuthFilter相繼刪除之后,已經(jīng)只剩下一個啟動類了,當(dāng)然,還有一個新加的監(jiān)控類。

難道是Spring Cloud Gateway本身有問題,咦,我好像發(fā)現(xiàn)了新大陸,這要是發(fā)現(xiàn)Spring Cloud Gateway有問題,以后又能吹噓一番了(內(nèi)心YY)。

既然,內(nèi)存分配沒有釋放,那我們就找到內(nèi)存分配的地方,打個斷點(diǎn)。

通過前面的分析,我們已經(jīng)知道使用的內(nèi)存分配器是UnpooledByteBufAllocator了,那就在它的newDirectBuffer()方法中打一個斷點(diǎn),因?yàn)槲覀冞@里是直接內(nèi)存泄漏了。

第十二步,一步一步調(diào)試

按照第十一步的思路,在UnpooledByteBufAllocator的newDirectBuffer()方法中打一個斷點(diǎn),一步一步調(diào)試,最后,來到了這個方法:

// io.netty.handler.codec.ByteToMessageDecoder.channelRead @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {     if (msg instanceof ByteBuf) {         CodecOutputList out = CodecOutputList.newInstance();         try {             first = cumulation == null;             // 1. 返回的是msg本身,msg是一個ByteBuf             cumulation = cumulator.cumulate(ctx.alloc(),                     first ? Unpooled.EMPTY_BUFFER : cumulation, (ByteBuf) msg);             // 2. 解碼,本文來源于工縱耗彤哥讀源碼             callDecode(ctx, cumulation, out);         } catch (DecoderException e) {             throw e;         } catch (Exception e) {             throw new DecoderException(e);         } finally {             if (cumulation != null && !cumulation.isReadable()) {                 numReads = 0;                 // 3. 釋放內(nèi)存                 cumulation.release();                 cumulation = null;             } else if (++ numReads >= discardAfterReads) {                 // We did enough reads already try to discard some bytes so we not risk to see a OOME.                 // See https://github.com/netty/netty/issues/4275                 numReads = 0;                 discardSomeReadBytes();             }              int size = out.size();             firedChannelRead |= out.insertSinceRecycled();             // 4. 讀取完out中剩余的值             fireChannelRead(ctx, out, size);             // 5. 回收out             out.recycle();         }     } else {         ctx.fireChannelRead(msg);     } }

這中間花了好幾個小時,特別是ChannelPipeLine里面一不小心就跳過去了,又得重新來過,真的是只能一步一步來。

這個方法主要是用來把ByteBuf轉(zhuǎn)換成Message,Message就是消息,可以理解為簡單的Java對象,主要邏輯在上面的代碼中都標(biāo)示出來了。

可以看到,這里有個cumulation.release();,它就是釋放內(nèi)存的地方,但是,并沒有釋放掉,在調(diào)用這行代碼之前,msg(=cumulation)的引用計數(shù)是4,釋放之后是2,所以,還有計數(shù),無法回收。

走完下面的4、5兩步,out都回收了,msg還是沒有被回收,問題肯定是出在這一塊。

一直在這里糾結(jié),包括decode里面的代碼都反反復(fù)復(fù)看了好多遍,這里沒有釋放的msg里面的內(nèi)容轉(zhuǎn)換之后的對象是DefaultHttpContent,它表示的是Http請求的body,不過這里是Http請求返回值的body。

這也是讓我很迷惑的一點(diǎn),我試了,Http請求的body好像沒有走到這塊邏輯,又反反復(fù)復(fù)地找Http請求的Body,搞了好久,一直沒有進(jìn)展。

到晚上9點(diǎn)多的時候,辦公室已經(jīng)沒什么人了,燈也關(guān)了(疫情期間,每個部門每天只能去幾個人),我也收拾下回家了。

第十三步,打車回家

在車上的時候,一直在想這個問題,回憶整個過程,會不會是我的方向錯了呢?

Spring Cloud Gateway出來也挺久了,沒聽說有內(nèi)存泄漏的問題啊,此時,我開始自我懷疑了。

不行,我回家得自己寫一個項目,使用Spring Cloud Gateway跑一下試試。

第十四步,寫一個使用Spring Cloud Gateway的項目

到家了,趕緊打開電腦,動手寫了一個使用Spring Cloud Gateway的項目和一個SpringBoot的項目,把監(jiān)控打開,把池化功能去掉,啟動項目,發(fā)送請求,觀察直接內(nèi)存。

used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0

納尼,阿西巴,到這里,已經(jīng)很明確了,不是Spring Cloud Gateway的問題,那是什么問題呢?

肯定是使用的姿勢不對,不過公司那個項目,也沒有別的什么東西了,類都被我刪完了,只剩下啟動類了。

哎不對,pom文件。

打開跳板機(jī),登錄到公司電腦,查看pom.xml,發(fā)現(xiàn)里面都是SpringBoot或者SpringCloud本身的一些引用。

嗯,不對,有個common包,分部自己寫的common包,點(diǎn)進(jìn)去,里面引用了三個jar包,其中,有一個特別扎眼,tomcat!!!!

哎喲我次奧,此時,我真的想罵娘,這都什么事兒~~

其實(shí),我在刪除AuthFilter的時候就應(yīng)該想到pom的問題的,當(dāng)時,只顧著YY Spring Cloud Gateway 可能有bug的問題了,一頭就扎進(jìn)去了。

我們知道,Spring Cloud Gateway使用的是Netty做為服務(wù)端接收請求,然后再轉(zhuǎn)發(fā)給下游系統(tǒng),這里引用tomcat會怎樣呢?還真是一件有趣的事呢。

第十五步,干掉tomcat

在pom文件中,把tomcat的jar包排除掉,重啟項目,發(fā)送請求,觀察直接內(nèi)存:

used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0 used direct memory: 0

哦了,沒有問題了,就是tomcat搗的鬼。

那么,tomcat是怎么搗鬼的呢?加了tomcat也能正常的響應(yīng)請求,請求也能正常的轉(zhuǎn)發(fā),返回給客戶端,而且,更可怕的是,內(nèi)部也確實(shí)是使用了Netty進(jìn)行請求的讀寫響應(yīng),真的有點(diǎn)神奇。

第十六步,發(fā)現(xiàn)新大陸

為了驗(yàn)證這個問題,我們還是先退出跳板機(jī),回到我自己的電腦,在pom中加入tomcat,啟動項目,咦,確實(shí)能起得來,好好玩兒~~

難道是tomcat和Netty同時監(jiān)聽了同一個端口,兩者都起來了?

觀察一下項目啟動日志:

Connected to the target VM, address: '127.0.0.1:52162', transport: 'socket'    .   ____          _            __ _ _  /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )   '  |____| .__|_| |_|_| |_\__, | / / / /  =========|_|==============|___/=/_/_/_/  :: Spring Boot ::        (v2.2.6.RELEASE)  2020-05-19 08:50:04.448  INFO 7896 --- [           main] com.alan.test.Application                : No active profile set, falling back to default profiles: default 2020-05-19 08:50:04.829  INFO 7896 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=082e67ca-d4c7-3a8c-b051-e806722fd225 2020-05-19 08:50:04.998  INFO 7896 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http) 2020-05-19 08:50:05.006  INFO 7896 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat] 2020-05-19 08:50:05.006  INFO 7896 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33] 2020-05-19 08:50:05.859  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [After] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Before] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Between] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Cookie] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Header] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Host] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Method] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Path] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Query] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [ReadBodyPredicateFactory] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [RemoteAddr] 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : 本文來源于工縱耗彤哥讀源碼 2020-05-19 08:50:05.860  INFO 7896 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [CloudFoundryRouteService] 2020-05-19 08:50:06.731  INFO 7896 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path '' 2020-05-19 08:50:07.304  INFO 7896 --- [           main] com.alan.test.Application                : Started Application in 4.271 seconds (JVM running for 5.0)

發(fā)現(xiàn)確實(shí)只啟動了tomcat,那它是怎么把請求移交給Netty來處理的呢?

第十七步,tomcat -> Netty

學(xué)習(xí)過NIO相關(guān)知識的同學(xué)應(yīng)該知道,NIO將SocketChannel分成了兩種,一種是ServerSocketChannel,一種是SocketChannel,其中,ServerSocketChannel是服務(wù)啟動的時候創(chuàng)建的,用來監(jiān)聽客戶端連接的到來,而SocketChannel就表示客戶端與服務(wù)端之間的連接。

看過NIO源碼的同學(xué)又知道,SocketChannel是通過ServerSocketChannel創(chuàng)建出來的。

看過Netty源碼的同學(xué)又知道,Netty根據(jù)不同的協(xié)議又把這些Channel分成了NioXxxChannel、EpollXxxChannel等等,針對每一種協(xié)議的Channel同樣分成NioServerSocketChannel、NioSocketChannel等。

而在Windows平臺下,默認(rèn)使用的是NioXxxChannel,而從上可知,NioSocketChannel應(yīng)該是通過NioServerSocketChannel創(chuàng)建出來的,如果是正常使用Netty,也確實(shí)是這樣的。

下圖是正常使用Netty時NioSocketChannel創(chuàng)建時的線程棧:

tomcat和Netty怎么解決內(nèi)存泄漏問題

不過,我們現(xiàn)在的場景是 tomcat + Netty,那又是怎樣的呢?

此時,在NioSocketChannel的構(gòu)造方法中打一個斷點(diǎn),發(fā)送一個請求,發(fā)現(xiàn)斷點(diǎn)到了NioSocketChannel的構(gòu)造方法中,觀察線程棧的情況(從下往上看):

tomcat和Netty怎么解決內(nèi)存泄漏問題

tomcat和Netty怎么解決內(nèi)存泄漏問題

tomcat和Netty怎么解決內(nèi)存泄漏問題

tomcat和Netty怎么解決內(nèi)存泄漏問題

tomcat和Netty怎么解決內(nèi)存泄漏問題

可以看到,經(jīng)過tomcat->spring->reactor->reactor-netty->netty,千轉(zhuǎn)百回之后,終于創(chuàng)建了NioSocketChannel。

這里的情況就有點(diǎn)復(fù)雜了,后面有時間,我們再詳細(xì)分析。

第十八步,內(nèi)存泄漏

從上面可以看出,Tomcat最終把請求的處理交給了Netty,但是為什么會內(nèi)存泄漏呢?這依然是個問題。

經(jīng)過我的對比檢測,問題還是出在第十二步的代碼那里,在使用正常的Netty請求時,在fireChannelRead()的里面會往NioEventLoop中添加一個任務(wù),叫作

MonoSendMany.SendManyInner.AsyncFlush:

final class AsyncFlush implements Runnable {     @Override     public void run() {         if (pending != 0) {             ctx.flush();         }     } }

這是用來把寫緩沖區(qū)的數(shù)據(jù)真正寫出去的(讀完了寫出去),同時,也會把寫緩沖區(qū)的數(shù)據(jù)清理掉,也就是調(diào)用了這個方法客戶端才能收到響應(yīng)的結(jié)果,而使用 tomcat + Netty 的時候,并沒有執(zhí)行這個任務(wù),數(shù)據(jù)就發(fā)送給了客戶端(猜測可能是通過tomcat的連接而不NioSocketChannel本身發(fā)送出去的),這是一個遺留問題,等后面再研究下了,現(xiàn)在腦子有點(diǎn)凌亂。

感謝各位的閱讀,以上就是“tomcat和Netty怎么解決內(nèi)存泄漏問題”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對tomcat和Netty怎么解決內(nèi)存泄漏問題這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

分享名稱:tomcat和Netty怎么解決內(nèi)存泄漏問題
URL分享:http://bm7419.com/article42/pcshec.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)網(wǎng)站建設(shè)、ChatGPT、企業(yè)網(wǎng)站制作電子商務(wù)、商城網(wǎng)站、云服務(wù)器

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)