偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

聊聊 Netty 零拷貝等技術(shù)對(duì)于內(nèi)存方面的優(yōu)化

開(kāi)發(fā)
本文將直接從源碼的角度分析一下 Netty 對(duì)于內(nèi)存方面的使用技巧,希望對(duì)你有所啟發(fā)。

Netty通過(guò)巧妙的內(nèi)存使用技巧盡可能節(jié)約內(nèi)存空間,進(jìn)而減少java中Full gc的STW的時(shí)間,由此間接的提升了程序的性能,本文也將直接從源碼的角度分析一下Netty對(duì)于內(nèi)存方面的使用技巧,希望對(duì)你有所啟發(fā)。

使用基本類(lèi)型替代包裝類(lèi)

內(nèi)存空間算是寶貴的系統(tǒng)資源,為了提升CPU加載數(shù)據(jù)效率以及節(jié)約內(nèi)存空間,對(duì)于某些常見(jiàn)的基本數(shù)據(jù)類(lèi)型,Netty都是能省則省,最直接的落地方案就是使用基本類(lèi)型替代包裝類(lèi)。

這其中totalPendingSize這個(gè)變量,它用于記錄那些待處理的數(shù)據(jù),為了節(jié)約內(nèi)存空間,記錄大小的類(lèi)型是long而非Long,通過(guò)這種方式避免了創(chuàng)建java對(duì)象(java對(duì)象包含對(duì)象頭的信息,相比基本類(lèi)型更占用內(nèi)存空間):

對(duì)此我們也給出這個(gè)變量的定義:

@SuppressWarnings("UnusedDeclaration")
    private volatile long totalPendingSize;

又因?yàn)樵撟侄涡枰WC線(xiàn)程安全,所以Netty設(shè)計(jì)者在此基礎(chǔ)上又將其設(shè)置為AtomicLong原子類(lèi)型,通過(guò)static關(guān)鍵字加以修飾,使所有實(shí)例共享一個(gè)變量,從而避免沒(méi)必要的創(chuàng)建開(kāi)銷(xiāo)和并發(fā)安全:

對(duì)此我們也給出源碼示例,即位于ChannelOutboundBuffer變量定義的位置:

//通過(guò)AtomicLongFieldUpdater修飾totalPendingSize
  private static final AtomicLongFieldUpdater<ChannelOutboundBuffer> TOTAL_PENDING_SIZE_UPDATER =
            AtomicLongFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "totalPendingSize");

動(dòng)態(tài)內(nèi)存調(diào)整

除上述內(nèi)存使用技巧以外,netty在進(jìn)行內(nèi)存分配時(shí)也用到的動(dòng)態(tài)調(diào)整的使用技巧,該設(shè)計(jì)理念比較簡(jiǎn)單,按照空間與分配思想:后續(xù)使用的內(nèi)存大小大概率是等同于本次使用的空間大小,所以Netty在調(diào)用record進(jìn)行內(nèi)存分配時(shí),如果發(fā)現(xiàn)縮小空間依然可以滿(mǎn)足要求,則進(jìn)行縮容,反之進(jìn)行擴(kuò)容,由此得到一個(gè)盡可能節(jié)約內(nèi)存空間且能滿(mǎn)足業(yè)務(wù)要求的數(shù)值:

private void record(int actualReadBytes) {
            //若實(shí)際需要的空間 <= 預(yù)縮小達(dá)到的尺寸,則對(duì)nextReceiveBufferSize進(jìn)行縮減
            if (actualReadBytes <= SIZE_TABLE[max(0, index - INDEX_DECREMENT)]) {
                if (decreaseNow) {
                    index = max(index - INDEX_DECREMENT, minIndex);
                    nextReceiveBufferSize = SIZE_TABLE[index];
                    decreaseNow = false;
                } else {
                    decreaseNow = true;
                }
            } else if (actualReadBytes >= nextReceiveBufferSize) {//如果所需空間大于nextReceiveBufferSize,則進(jìn)行擴(kuò)容
                index = min(index + INDEX_INCREMENT, maxIndex);
                nextReceiveBufferSize = SIZE_TABLE[index];
                decreaseNow = false;
            }
        }

應(yīng)用層面的zero-copy

內(nèi)存拷貝也是存在一定的時(shí)間開(kāi)銷(xiāo),例如我們現(xiàn)在有一個(gè)字符串的數(shù)據(jù)需要將byte1和byte2拼接起來(lái)才能得到,按照傳統(tǒng)的實(shí)現(xiàn)思路,我們需要開(kāi)發(fā)一個(gè)足夠容納byte1和byte2的內(nèi)存空間,然后將byte1和byte2一并寫(xiě)入,這種做法有著如下耗時(shí)點(diǎn):

  • 開(kāi)辟內(nèi)存空間所占用的時(shí)間。
  • 將byte1內(nèi)存新開(kāi)辟空間的耗時(shí)。
  • 將byte2寫(xiě)入新開(kāi)辟的內(nèi)存空間耗時(shí)。

而Netty則不是這樣做,它的設(shè)計(jì)思路是直接將兩個(gè)數(shù)組,邏輯上組合,即通過(guò)一個(gè)數(shù)組指向這兩個(gè)引用,從邏輯上視為一個(gè)整體,而不是物理操作上的組合:

對(duì)此我們給出CompositeByteBuf的addComponent0方法,可以看到對(duì)于需要組合的數(shù)據(jù)buffer,它會(huì)通過(guò)addComp方法將這個(gè)ByteBuf 存到CompositeByteBuf底層的數(shù)組中,由此保證數(shù)據(jù)邏輯上的一致:

private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffer) {
        assert buffer != null;
        boolean wasAdded = false;
        try {
            checkComponentIndex(cIndex);

            //將其包裝為Component 
            Component c = newComponent(ensureAccessible(buffer), 0);
            int readableBytes = c.length();

           //......
   //添加到CompositeByteBuf底層的components數(shù)組中,通過(guò)邏輯完成組合
            addComp(cIndex, c);
           //......
            return cIndex;
        } finally {
          //......
        }
    }

//添加到components數(shù)組中保證邏輯上的一致
private void addComp(int i, Component c) {
        //......
        components[i] = c;
    }

使用堆外內(nèi)存

將數(shù)據(jù)存放在JVM非堆內(nèi)存空間,通過(guò)減少?zèng)]必要的GC確保操作和執(zhí)行性能的高效,這也是Netty中對(duì)于內(nèi)存方面的優(yōu)化,這其中最經(jīng)典的就是PooledHeapByteBuf,它直接操作的就是堆外內(nèi)存的數(shù)據(jù):

對(duì)此我們也給處PooledDirectByteBuf 獲取直接內(nèi)存的源碼實(shí)現(xiàn):

//從內(nèi)存池中獲取直接內(nèi)存空間返回給用戶(hù)使用
  static PooledDirectByteBuf newInstance(int maxCapacity) {
        PooledDirectByteBuf buf = RECYCLER.get();
        buf.reuse(maxCapacity);
        return buf;
    }

需要補(bǔ)充的是,這種做法也存在的一定的風(fēng)險(xiǎn):

  • 創(chuàng)建速度慢。
  • 存放在非堆內(nèi)存空間,使用不當(dāng)可能造成內(nèi)存泄漏。

內(nèi)存池化復(fù)用

上文的堆內(nèi)存就是PooledHeapByteBuf即池化過(guò)的內(nèi)存,通過(guò)池化:

  • 保證對(duì)象復(fù)用,減小沒(méi)必要的創(chuàng)建開(kāi)銷(xiāo)。
  • 提升程序并發(fā)執(zhí)行性能。

對(duì)此我們給出相應(yīng)的源碼實(shí)現(xiàn):

//初始化直接內(nèi)存池化工廠(chǎng)RECYCLER 
private static final ObjectPool<PooledDirectByteBuf> RECYCLER = ObjectPool.newPool(
            new ObjectCreator<PooledDirectByteBuf>() {
        @Override
        public PooledDirectByteBuf newObject(Handle<PooledDirectByteBuf> handle) {
            return new PooledDirectByteBuf(handle, 0);
        }
    });


//從內(nèi)存池中獲取直接內(nèi)存空間返回給用戶(hù)使用
  static PooledDirectByteBuf newInstance(int maxCapacity) {
    //從內(nèi)存池中獲取直接內(nèi)存空間
        PooledDirectByteBuf buf = RECYCLER.get();
        buf.reuse(maxCapacity);
        return buf;
    }

對(duì)jdk零拷貝的封裝

我們?cè)谏鲜鏊v的零復(fù)制更多強(qiáng)調(diào)的是應(yīng)用層面上的零復(fù)制,也就是通過(guò)減少應(yīng)用層面上數(shù)據(jù)的拷貝提升程序的執(zhí)行效率。實(shí)際上Netty也有基于操作系統(tǒng)層面的零拷貝實(shí)現(xiàn),這其中最典型的實(shí)現(xiàn)就是DefaultFileRegion的transferTo函數(shù),它底層調(diào)用JDK自帶的NIO零拷貝方法transferTo實(shí)現(xiàn)當(dāng)前文件數(shù)據(jù)通過(guò)sendfile調(diào)用傳輸?shù)絪ocket通道中,由此避免數(shù)據(jù)傳輸時(shí)多次切態(tài)、內(nèi)核緩沖區(qū)和用戶(hù)緩沖區(qū)來(lái)回拷貝的開(kāi)銷(xiāo):

對(duì)此我們也給出DefaultFileRegion類(lèi)中transferTo的源碼,可以看到其底層就是將JDK默認(rèn)的NIO零拷貝方法進(jìn)行封裝,將DefaultFileRegion封裝的FileChannel 的文件數(shù)據(jù)拷貝到target的文件通道中,其底層就用到內(nèi)核函數(shù)sendfile:

private FileChannel file;

 @Override
    public long transferTo(WritableByteChannel target, long position) throws IOException {
        //......

        long written = file.transferTo(this.position + position, count, target);
        if (written > 0) {
            transferred += written;
        } else if (written == 0) {
           //......
        }
        return written;
    }
責(zé)任編輯:趙寧寧 來(lái)源: 寫(xiě)代碼的SharkChili
相關(guān)推薦

2024-11-22 00:09:15

2016-11-23 19:09:39

javanetty

2018-08-15 10:29:58

NettyJDK內(nèi)存

2024-06-07 08:10:14

Netty操作系統(tǒng)零拷貝

2022-09-23 08:47:01

DMA網(wǎng)卡CPU

2022-05-05 13:57:43

Buffer設(shè)備MYSQL

2022-05-16 08:22:37

零拷貝Netty

2021-06-08 07:45:44

Go語(yǔ)言優(yōu)化

2009-12-30 17:30:43

EPON技術(shù)

2024-12-26 17:04:47

2021-08-26 06:57:53

零拷貝技術(shù)磁盤(pán)

2023-12-02 20:41:32

內(nèi)存kube

2020-07-06 15:10:05

Linux拷貝代碼

2020-07-23 15:40:54

Linux零拷貝文件

2022-09-27 13:34:49

splice零拷貝原理

2013-04-27 17:09:29

安全管理IT技術(shù)

2010-09-02 10:32:41

2020-08-03 10:53:25

存儲(chǔ)容器虛擬機(jī)

2023-04-17 16:40:12

能源管理綠色數(shù)字化轉(zhuǎn)型

2024-11-28 10:40:26

零拷貝技術(shù)系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)