日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Netty的引用计数对象

發布時間:2025/3/15 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Netty的引用计数对象 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

從Netty 4起,對象的生命周期由它們的引用計數來管理,因此,一旦對象不再被引用后,Netty 會將它(或它共享的資源)歸還到對象池(或對象分配器)。在垃圾回收和引用隊列不能保證這么有效、實時的不可達性檢測的情況下,引用計數以犧牲輕微的便利性為代價,提供了另一種可選的解決方案。 最值得注意的類型是ByteBuf,它正是利用了引用計數來提升內存分配和釋放的性能。

一、引用計數基本原理

一個新創建的引用計數對象的初始引用計數是1。

ByteBuf buf = ctx.alloc().directbuffer(); assert buf.refCnt() == 1;

當你釋放掉引用計數對象,它的引用次數減1.如果一個對象的引用計數到達0,該對象就會被 釋放或者歸還到創建它的對象池。

assert buf.refCnt() == 1; // release() returns true only if the reference count becomes 0. boolean destroyed = buf.release(); assert destroyed; assert buf.refCnt() == 0;
  • 懸掛引用
  • 訪問引用計數為0的引用計數對象會觸發一次IllegalReferenceCountException:

    assert buf.refCnt() == 0; try { buf.writeLong(0xdeadbeef); throw new Error("should not reach here"); } catch (IllegalReferenceCountExeception e) { // Expected }
  • 增加引用計數
  • 只要引用計數對象未被銷毀,就可以通過調用retain()方法來增加引用次數:

    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;
  • 誰來銷毀
    一般的原則是,最后訪問引用計數對象的部分負責對象的銷毀。更具體地來說:
    • 如果一個[發送]組件要傳遞一個引用計數對象到另一個[接收]組件,發送組件通常不需要 負責去銷毀對象,而是將這個銷毀的任務推延到接收組件
    • 如果一個組件消費了一個引用計數對象,并且不知道誰會再訪問它(例如,不會再將引用 發送到另一個組件),那么,這個組件負責銷毀工作
      這里有一個簡單的示例:
    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; }

    二、子緩沖區(Derived buffers)

    調用ByteBuf.duplicate(),ByteBuf.slice()和ByteBuf.order(ByteOrder)三個方法, 會創建一個子緩沖區,子緩沖區共享父緩沖區的內存區域。子緩沖區沒有自己的引用計數,而是共享父緩沖區的引用計數。

    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;

    但是,調用ByteBuf.copy()和ByteBuf.readBytes(int)創建的并不是子緩沖區,返回的 ByteBuf緩沖區是需要被釋放的。 需要注意,父緩沖區和它的子緩沖區共享引用計數,創建子緩沖區并不會增加引用計數。 因此,當你將子緩沖區傳到應用中的其他組件,必須先調用retain()。

    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接口
    有時候,ByteBuf被緩沖區容器(buffer holder)持有,像DatagramPacket、HttpComponent和WebSocketFrame。 這些類型都繼承自一個通用接口,叫做ByteBufHolder。 緩沖區容器(buffer holder)共享它持有的緩沖區的引用計數,和子緩沖區一樣。

  • Channel-handler中的引用計數

    • 入口消息

    當一個事件循環(event loop)讀取數據并寫入到ByteBuf,在觸發一次channelRead()事件后,應該由對應pipeline的 ChannelHandler負責去釋放緩沖區的內存。因此,消費接收數據的handler應該在它channelRead()方法中調用數據的 release()方法。

    public void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf buf = (ByteBuf) msg;try {...} finally {buf.release();}}

    在“誰負責銷毀”一節中我們提到,如果你的handler將緩沖區(或者其他任何引用計數對象)傳遞到下一個handler, 那么你不需要負責去釋放。

    public void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf buf = (ByteBuf) msg;...ctx.fireChannelRead(buf); }

    需要注意的是,ByteBuf并不是Netty中唯一的引用計數類型。如果你在與解碼程序(decoder)生成的消息打交道,這些消息一樣可能 是引用計數的。

    / 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();}} }

    如果你有疑慮,或者你想簡化釋放消息內存的過程,你可以使用ReferenceCountUtil.release():

    public void channelRead(ChannelHandlerContext ctx, Object msg) {try {...} finally {ReferenceCountUtil.release(msg);} }

    同樣地,你可以考慮繼承SimpleChannelHandler,它會幫你調用ReferenceCountUtil.release()釋放所有 你接收到的消息內存。

    • 出口消息

    與入口消息不同的是,出口消息是在你的應用中創建的,由Netty負責在將消息發送出去后釋放掉。但是,如果你 有攔截寫請求的handler程序,則需要保證正確釋放中間對象(例如,編碼程序)。

    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);} }

    三、內存泄漏

    引用計數的缺點是,引用計數對象容易發生泄露。因為JVM并不知道Netty的引用計數實現,當引用計數對象不可達時,JVM就會將它們GC掉,即時此時它們的引用計數并不為0。一旦對象被GC就不能再訪問,也就不能 歸還到緩沖池,所以會導致內存泄露。 慶幸的是,盡管發現內存泄露很難,但是Netty會對分配的緩沖區的1%進行采樣,來檢查你的應用中是否存在內存 泄露。

    文章轉自

    總結

    以上是生活随笔為你收集整理的Netty的引用计数对象的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。