摘要:轉(zhuǎn)發(fā)自
轉(zhuǎn)發(fā)自 http://netty.io/wiki/referenc...
Since Netty version 4, the life cycle of certain objects are managed by their reference counts, so that Netty can return them (or their shared resources) to an object pool (or an object allocator) as soon as it is not used anymore. Garbage collection and reference queues do not provide such efficient real-time guarantee of unreachability while reference-counting provides an alternative mechanism at the cost of slight inconvenience.
ByteBuf is the most notable type which takes advantage of reference counting to improve the allocation and deallocation performance, and this page will explain how reference counting in Netty works using ByteBuf.
Basics of reference countingThe initial reference count of a new reference-counted object is 1:
ByteBuf buf = ctx.alloc().directBuffer(); assert buf.refCnt() == 1;
When you release the reference-counted object, its reference count is decreased by 1. If the reference count reaches at 0, the reference-counted object is deallocated or returned to the object pool it came from:
assert buf.refCnt() == 1; // release() returns true only if the reference count becomes 0. boolean destroyed = buf.release(); assert destroyed; assert buf.refCnt() == 0;Dangling reference
Attempting to access the reference-counted object whose reference count is 0 will trigger an IllegalReferenceCountException:
assert buf.refCnt() == 0; try { buf.writeLong(0xdeadbeef); throw new Error("should not reach here"); } catch (IllegalReferenceCountExeception e) { // Expected }Increasing the reference count
reference count can also be incremented via the retain() operation as long as it is not destroyed yet:
ByteBuf buf = ctx.alloc().directBuffer(); assert buf.refCnt() == 1; buf.retain(); assert buf.refCnt() == 2; boolean destroyed = buf.release(); assert !destroyed; assert buf.refCnt() == 1;Who destroys it?
The general rule of thumb is that the party who accesses a reference-counted object lastly is responsible for the destruction of the reference-counted object. More specifically:
If a [sending] component is supposed to pass a reference-counted object to another [receiving] component, the sending component usually does not need to destroy it but defers that decision to the receiving component.
If a component consumes a reference-counted object and knows nothing else will access it anymore (i.e., does not pass along a reference to yet another component), the component should destroy it.
Here is a simple example:
public ByteBuf a(ByteBuf input) { input.writeByte(42); return input; } public ByteBuf b(ByteBuf input) { try { output = input.alloc().directBuffer(input.readableBytes() + 1); output.writeBytes(input); output.writeByte(42); return output; } finally { input.release(); } } public void c(ByteBuf input) { System.out.println(input); input.release(); } public void main() { ... ByteBuf buf = ...; // This will print buf to System.out and destroy it. c(b(a(buf))); assert buf.refCnt() == 0; }
Action Who should release? Who released?
main() creates buf buf→main()
main() calls a() with buf buf→a()
a() returns buf merely. buf→main()
main() calls b() with buf buf→b()
b() returns the copy of buf buf→b(), copy→main() b() releases buf
main() calls c() with copy copy→c()
c() swallows copy copy→c() c() releases copy
Derived buffersByteBuf.duplicate(), ByteBuf.slice() and ByteBuf.order(ByteOrder) create a derived buffer which shares the memory region of the parent buffer. A derived buffer does not have its own reference count but shares the reference count of the parent buffer.
ByteBuf parent = ctx.alloc().directBuffer(); ByteBuf derived = parent.duplicate(); // Creating a derived buffer does not increase the reference count. assert parent.refCnt() == 1; assert derived.refCnt() == 1;
In contrast, ByteBuf.copy() and ByteBuf.readBytes(int) are not derived buffers. The returned ByteBuf is allocated will need to be released.
Note that a parent buffer and its derived buffers share the same reference count, and the reference count does not increase when a derived buffer is created. Therefore, if you are going to pass a derived buffer to other component of your application, you"ll have to call retain() it first.
ByteBuf parent = ctx.alloc().directBuffer(512); parent.writeBytes(...); try { while (parent.isReadable(16)) { ByteBuf derived = parent.readSlice(16); derived.retain(); process(derived); } } finally { parent.release(); } ... public void process(ByteBuf buf) { ... buf.release(); }ByteBufHolder interface
Sometimes, a ByteBuf is contained by a buffer holder, such as DatagramPacket, HttpContent, and WebSocketframe. Those types extend a common interface called ByteBufHolder.
buffer holder shares the reference count of the buffer it contains, just like a derived buffer.
Reference-counting in ChannelHandlerInbound messages
When an event loop reads data into a ByteBuf and triggers a channelRead() event with it, it is the responsibility of the ChannelHandler in the corresponding pipeline to release the buffer. Therefore, the handler that consumes the received data should call release() on the data in its channelRead() handler method:
public void channelRead(ChannelHandlerContext ctx, Object msg) { ByteBuf buf = (ByteBuf) msg; try { ... } finally { buf.release(); } }
As explained in the "Who destroys?" section of this document, if your handler passes the buffer (or any reference-counted object) to the next handler, you don"t need to release it:
public void channelRead(ChannelHandlerContext ctx, Object msg) { ByteBuf buf = (ByteBuf) msg; ... ctx.fireChannelRead(buf); }
Note that ByteBuf isn"t the only reference-counted type in Netty. If you are dealing with the messages generated by decoders, it is very likely that the message is also reference-counted:
// Assuming your handler is placed next to `HttpRequestDecoder` public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof HttpRequest) { HttpRequest req = (HttpRequest) msg; ... } if (msg instanceof HttpContent) { HttpContent content = (HttpContent) msg; try { ... } finally { content.release(); } } }
If you are in doubt or you want to simplify releasing the messages, you can use ReferenceCountUtil.release():
public void channelRead(ChannelHandlerContext ctx, Object msg) { try { ... } finally { ReferenceCountUtil.release(msg); } }
Alternatively, you could consider extending SimpleChannelHandler which calls ReferenceCountUtil.release(msg) for all messages you receive.
Outbound messages
Unlike inbound messages, outbound messages are created by your application, and it is the responsibility of Netty to release these after writing them out to the wire. However, the handlers that intercept your write requests should make sure to release any intermediary objects properly. (e.g. encoders)
// Simple-pass through public void write(ChannelHandlerContext ctx, Object message, ChannelPromise promise) { System.err.println("Writing: " + message); ctx.write(message, promise); } // Transformation public void write(ChannelHandlerContext ctx, Object message, ChannelPromise promise) { if (message instanceof HttpContent) { // Transform HttpContent to ByteBuf. HttpContent content = (HttpContent) message; try { ByteBuf transformed = ctx.alloc().buffer(); .... ctx.write(transformed, promise); } finally { content.release(); } } else { // Pass non-HttpContent through. ctx.write(message, promise); } }Troubleshooting buffer leaks
The disadvantage of reference counting is that it is easy to leak the reference-counted objects. Because JVM is not aware of the reference counting Netty implements, it will automatically GC them once they become unreachable even if their reference counts are not zero. An object once garbage collected cannot be resurrected, and thus cannot be returned to the pool it came from and thus will produce memory leak.
Fortunately, despite its difficulty of finding leaks, Netty will by default sample about 1% of buffer allocations to check if there is a leak in your application. In case of leak, you will find the following log message:
LEAK: ByteBuf.release() was not called before it"s garbage-collected. Enable advanced leak reporting to find out where the leak occurred. To enable advanced leak reporting, specify the JVM option "-Dio.netty.leakDetectionLevel=advanced" or call ResourceLeakDetector.setLevel()
Relaunch your application with the JVM option mentioned above, then you"ll see the recent locations of your application where the leaked buffer was accessed. The following output shows a leak from our unit test (XmlFrameDecoderTest.testDecodeWithXml()):
Running io.netty.handler.codec.xml.XmlFrameDecoderTest 15:03:36.886 [main] ERROR io.netty.util.ResourceLeakDetector - LEAK: ByteBuf.release() was not called before it"s garbage-collected. Recent access records: 1 #1: io.netty.buffer.AdvancedLeakAwareByteBuf.toString(AdvancedLeakAwareByteBuf.java:697) io.netty.handler.codec.xml.XmlFrameDecoderTest.testDecodeWithXml(XmlFrameDecoderTest.java:157) io.netty.handler.codec.xml.XmlFrameDecoderTest.testDecodeWithTwoMessages(XmlFrameDecoderTest.java:133) ... Created at: io.netty.buffer.UnpooledByteBufAllocator.newDirectBuffer(UnpooledByteBufAllocator.java:55) io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:155) io.netty.buffer.UnpooledUnsafeDirectByteBuf.copy(UnpooledUnsafeDirectByteBuf.java:465) io.netty.buffer.WrappedByteBuf.copy(WrappedByteBuf.java:697) io.netty.buffer.AdvancedLeakAwareByteBuf.copy(AdvancedLeakAwareByteBuf.java:656) io.netty.handler.codec.xml.XmlFrameDecoder.extractFrame(XmlFrameDecoder.java:198) io.netty.handler.codec.xml.XmlFrameDecoder.decode(XmlFrameDecoder.java:174) io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:227) io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:140) io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:74) io.netty.channel.embedded.EmbeddedEventLoop.invokeChannelRead(EmbeddedEventLoop.java:142) io.netty.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:317) io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846) io.netty.channel.embedded.EmbeddedChannel.writeInbound(EmbeddedChannel.java:176) io.netty.handler.codec.xml.XmlFrameDecoderTest.testDecodeWithXml(XmlFrameDecoderTest.java:147) io.netty.handler.codec.xml.XmlFrameDecoderTest.testDecodeWithTwoMessages(XmlFrameDecoderTest.java:133) ... If you use Netty 5 or above, an additional information is provided to help you find which handler handled the leaked buffer lastly. The following example shows that the leaked buffer was handled by the handler whose name is EchoServerHandler#0 and then garbage-collected, which means it is likely that EchoServerHandler#0 forgot to release the buffer: 12:05:24.374 [nioEventLoop-1-1] ERROR io.netty.util.ResourceLeakDetector - LEAK: ByteBuf.release() was not called before it"s garbage-collected. Recent access records: 2 #2: Hint: "EchoServerHandler#0" will handle the message from this point. io.netty.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:329) io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846) io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:133) io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:485) io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:452) io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:346) io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:794) java.lang.Thread.run(Thread.java:744) #1: io.netty.buffer.AdvancedLeakAwareByteBuf.writeBytes(AdvancedLeakAwareByteBuf.java:589) io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:208) io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:125) io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:485) io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:452) io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:346) io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:794) java.lang.Thread.run(Thread.java:744) Created at: io.netty.buffer.UnpooledByteBufAllocator.newDirectBuffer(UnpooledByteBufAllocator.java:55) io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:155) io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:146) io.netty.buffer.AbstractByteBufAllocator.ioBuffer(AbstractByteBufAllocator.java:107) io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:123) io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:485) io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:452) io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:346) io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:794) java.lang.Thread.run(Thread.java:744)Leak detection levels
There are currently 4 levels of leak detection:
DISABLED - disables leak detection completely. Not recommended.
SIMPLE - tells if there is a leak or not for 1% of buffers. Default.
ADVANCED - tells where the leaked buffer was accessed for 1% of buffers.
PARANOID - Same with ADVANCED except that it"s for every single buffer. Useful for automated testing phase. You could fail the build if the build output contains "LEAK:".
You can specify the leak detection level as a JVM option -Dio.netty.leakDetection.level
java -Dio.netty.leakDetection.level=advanced ...
NOTE: This property used to be called io.netty.leakDetectionLevel.
Run your unit tests and integration tests at PARANOID leak detection
level, as well as at SIMPLE level.
Canary your application before rolling out to the entire cluster at
SIMPLE level for a reasonably long time to see if there"s a leak.
If there is a leak, canary again at ADVANCED level to get some hints
about where the leak is coming from.
Do not deploy an application with a leak to the entire cluster.
Fixing leaks in unit testsIt is very easy to forget to release a buffer or a message in a unit test. It will generate a leak warning, but it does not necessarily mean that your application has a leak. Instead of wrapping your unit tests with try-finally blocks to release all buffers, you can use ReferenceCountUtil.releaseLater() utility method:
import static io.netty.util.ReferenceCountUtil.*; @Test public void testSomething() throws Exception { // ReferenceCountUtil.releaseLater() will keep the reference of buf, // and then release it when the test thread is terminated. ByteBuf buf = releaseLater(Unpooled.directBuffer(512)); ... }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/69028.html
摘要:當你從讀取時,它的將會被遞增已經(jīng)被讀取的字節(jié)數(shù)。達到和位于同一位置,表示我們到達可以讀取的數(shù)據(jù)的末尾。該應用程序可以選擇為多個消息重用相同的消息主體。 ByteBuffer 當我們進行數(shù)據(jù)傳輸?shù)臅r候,往往需要使用到緩沖區(qū),常用的緩沖區(qū)就是JDK NIO類庫提供的java.nio.Buffer。 showImg(https://segmentfault.com/img/bVbbz8p?w...
摘要:服務器構(gòu)成至少一個該組件實現(xiàn)了服務器對從客戶端接受的數(shù)據(jù)的處理,即它的業(yè)務邏輯引導配置服務器的啟動代碼。至少,它會將服務器綁定到它要監(jiān)聽連接請求的端口上。需要注意的是,由服務器發(fā)送的消息可能會被分塊接受。 Netty服務器構(gòu)成 至少一個ChannelHandler——該組件實現(xiàn)了服務器對從客戶端接受的數(shù)據(jù)的處理,即它的業(yè)務邏輯 引導——配置服務器的啟動代碼。至少,它會將服務器綁定...
摘要:隨著狀態(tài)發(fā)生變化,相應的產(chǎn)生。這些被轉(zhuǎn)發(fā)到中的來采取相應的操作。當收到數(shù)據(jù)或相關(guān)的狀態(tài)改變時,這些方法被調(diào)用,這些方法和的生命周期密切相關(guān)。主要由一系列組成的。采用的線程模型,在同一個線程的中處理所有發(fā)生的事。 「博客搬家」 原地址: 簡書 原發(fā)表時間: 2017-05-05 學習了一段時間的 Netty,將重點與學習心得總結(jié)如下,本文主要總結(jié)ChannelHandler 及 E...
摘要:目前為止,我們已經(jīng)完成了一半的工作,剩下的就是在方法中啟動服務器。第一個通常被稱為,負責接收已到達的。這兩個指針恰好標記著數(shù)據(jù)的起始終止位置。 前言 本篇翻譯自netty官方Get Start教程,一方面能把好的文章分享給各位,另一方面能鞏固所學的知識。若有錯誤和遺漏,歡迎各位指出。 https://netty.io/wiki/user-gu... 面臨的問題 我們一般使用專用軟件或者...
摘要:使用來優(yōu)化套接字操作,盡可能消除由的緩沖區(qū)實現(xiàn)所導致的性能以及內(nèi)存使用率的懲罰,這種優(yōu)化發(fā)生在的核心代碼中,不會被暴露出來。當前將會被增加所寫入的字節(jié)數(shù)。 ByteBuf是Java NIO ByteBuffer的替代品,是網(wǎng)絡(luò)數(shù)據(jù)基本單位字節(jié)的容器。 ByteBuf的API Netty的數(shù)據(jù)處理API通過兩個組件暴漏:抽象類ByteBuf和接口ByteBufHolder ByteBuf...
閱讀 2181·2021-11-24 10:26
閱讀 2792·2021-11-23 09:51
閱讀 2907·2021-10-08 10:05
閱讀 1683·2021-09-22 15:18
閱讀 1619·2019-08-29 18:45
閱讀 2143·2019-08-29 18:40
閱讀 3332·2019-08-29 16:16
閱讀 2849·2019-08-29 14:21