拍案叫絕!Jackson內存設計中的組合拳——TextBuffer和BufferRecycler

Jackson作爲Java三大Json框架之一,也是SpringBoot的默認序列化框架,具有速度快、內存佔用低等特點。在rest服務盛行的今天,序列化和反序列化操作在系統中是一個極其常見的操作,這部分帶來的內存開銷也是一塊重點。今天來看一下Jsckson中爲了節省內存的神操作之一——TextBufferBufferRecycler的組合拳。

TextBuffer

TextBuffer是幹嘛的?翻譯官方註解,可以看作是一個StringBuffer(其實更應該是StringBuilder,因爲它也是線程不安全的),但是它有一些騷操作。
StringBuffer在添加內容時,會對char[]進行容量判斷,如果不足會進行擴容,並通過數組複製將原數據複製到新數組中。

private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

擴容複製會帶來額外的性能開銷。
TextBuffer就避免了這種複製,它是怎麼做到的?
TextBuffer使用了分段char[]的技術來緩衝內容,其內部持有一個列表private ArrayList<char[]> _segments;用來存放數組,噹噹前數組不夠用了,新申請一個放後面的內容,然後加入到分段列表中。

是不是很簡單?!你咋沒想到呢?

你以爲這就完了嗎?還有另一個小夥伴BufferRecycler沒登場呢!

BufferRecycler

從名字看出,這應該是一個用來循環利用buffer的一個類?
沒錯!TextBuffer在需要申請新的空間時,不是自己直接申請內存的,而是向BufferRecycler申請一個新的數組空間。當TextBuffer清空數據時,不會釋放空間,而是將空間還給BufferRecycler,來重新利用數組空間。
BufferRecycler自身通過二維數組維護了多個數組的引用,當有其他對象來申請數組空間時,BufferRecycler會將自身持有的數組給對方,如果沒有了則新建。當其他對象用完了數組後會將該數組空間的引用還給BufferRecyclerBufferRecycler將該引用記錄到自己的二維數組中,以便重新分配。

public char[] allocCharBuffer(int ix, int minSize) {
        final int DEF_SIZE = charBufferLength(ix);
        if (minSize < DEF_SIZE) {
            minSize = DEF_SIZE;
        }
        //從二維數組中拿到buffer引用
        char[] buffer = _charBuffers[ix];
        if (buffer == null || buffer.length < minSize) {
        	//空則進行新分配
            buffer = calloc(minSize);
        } else {
        	//不爲空自己不再持有該buffer引用,表示將其分配出去了
            _charBuffers[ix] = null;
        }
        //分配buffer
        return buffer;
    }
public void releaseCharBuffer(int ix, char[] buffer) {
		//將歸還過來的buffer的引用記錄到自身的二維數組中以便進行復用
        _charBuffers[ix] = buffer;
    }

很簡單的設計理念,但簡單中透漏着細節和優雅,拍案叫絕!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章