Netty源碼解析 -- 對象池Recycler實現原理

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於在Java中創建一個實例的消耗不小,很多框架爲了提高性能都使用對象池,Netty也不例外。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文主要分析Netty對象池Recycler的實現原理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"源碼分析基於Netty 4.1.52","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"緩存對象管理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Recycler的內部類Stack負責管理緩存對象。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Stack關鍵字段","attrs":{}}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"// Stack所屬主線程,注意這裏使用了WeakReference\nWeakReference threadRef; \n// 主線程回收的對象\nDefaultHandle>[] elements;\n// elements最大長度\nint maxCapacity;\n// elements索引\nint size;\n// 非主線程回收的對象\nvolatile WeakOrderQueue head; ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Recycler將一個Stack劃分給某個主線程,主線程直接從Stack#elements中存取對象,而非主線程回收對象則存入WeakOrderQueue中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"threadRef字段使用了WeakReference,當主線程消亡後,該字段指向對象就可以被垃圾回收。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DefaultHandle,對象的包裝類,在Recycler中緩存的對象都會包裝成DefaultHandle類。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"head指向的WeakOrderQueue,用於存放其他線程的對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WeakOrderQueue主要屬性","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// Head#link指向Link鏈表首對象\nHead head; \n// 指向Link鏈表尾對象\nLink tail;\n// 指向WeakOrderQueue鏈表下一對象\nWeakOrderQueue next;\n// 所屬線程\nWeakReference owner;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Link中也有一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"DefaultHandle>[] elements","attrs":{}}],"attrs":{}},{"type":"text","text":"字段,負責存儲數據。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,Link繼承了AtomicInteger,AtomicInteger的值存儲elements的最新索引。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WeakOrderQueue也是屬於某個線程,並且WeakOrderQueue繼承了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WeakReference","attrs":{}}],"attrs":{}},{"type":"text","text":",當所屬線程消亡時,對應WeakOrderQueue也可以被垃圾回收。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意:每個WeakOrderQueue都只屬於一個Stack,並且只屬於一個非主線程。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/57/57137b81ea4b018b70d5119f6f9043f2.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"thread2要存放對象到Stack1中,只能存放在WeakOrderQueue1","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"thread1要存放對象到Stack2中,只能存放在WeakOrderQueue3","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"回收對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DefaultHandle#recycle -> Stack#push","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"void push(DefaultHandle> item) {\n Thread currentThread = Thread.currentThread();\n if (threadRef.get() == currentThread) {\n // #1\n pushNow(item);\n } else {\n // #2\n pushLater(item, currentThread);\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1","attrs":{}}],"attrs":{}},{"type":"text","text":" 當前線程是主線程,直接將對象加入到Stack#elements中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2","attrs":{}}],"attrs":{}},{"type":"text","text":" 當前線程非主線程,需要將對象放到對應的WeakOrderQueue中","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void pushLater(DefaultHandle> item, Thread thread) {\n ...\n // #1\n Map, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();\n WeakOrderQueue queue = delayedRecycled.get(this);\n if (queue == null) {\n // #2\n if (delayedRecycled.size() >= maxDelayedQueues) {\n delayedRecycled.put(this, WeakOrderQueue.DUMMY);\n return;\n }\n // #3\n if ((queue = newWeakOrderQueue(thread)) == null) {\n return;\n }\n delayedRecycled.put(this, queue);\n } else if (queue == WeakOrderQueue.DUMMY) {\n // #4\n return;\n }\n // #5\n queue.add(item);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1","attrs":{}}],"attrs":{}},{"type":"text","text":" DELAYED_RECYCLED是一個FastThreadLocal,可以理解爲Netty中的ThreadLocal優化類。它爲每個線程維護了一個Map,存儲每個Stack和對應WeakOrderQueue。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有這裏獲取的delayedRecycled變量是僅用於當前線程的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而delayedRecycled.get獲取的WeakOrderQueue,是以Thread + Stack作爲維度區分的,只能是一個線程操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2","attrs":{}}],"attrs":{}},{"type":"text","text":" 當前WeakOrderQueue數量超出限制,添加WeakOrderQueue.DUMMY作爲標記","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3","attrs":{}}],"attrs":{}},{"type":"text","text":" 構造一個WeakOrderQueue,加入到Stack#head指向的WeakOrderQueue鏈表中,並放入DELAYED_RECYCLED。這時是需要一下同步操作的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#4","attrs":{}}],"attrs":{}},{"type":"text","text":" 遇到WeakOrderQueue.DUMMY標記對象,直接拋棄對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#5","attrs":{}}],"attrs":{}},{"type":"text","text":" 將緩存對象添加到WeakOrderQueue中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WeakOrderQueue#add","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"void add(DefaultHandle> handle) {\n handle.lastRecycledId = id;\n\n // #1\n if (handleRecycleCount < interval) {\n handleRecycleCount++;\n return;\n }\n handleRecycleCount = 0;\n\n \n Link tail = this.tail;\n int writeIndex;\n // #2\n if ((writeIndex = tail.get()) == LINK_CAPACITY) {\n Link link = head.newLink();\n if (link == null) {\n return;\n }\n this.tail = tail = tail.next = link;\n writeIndex = tail.get();\n }\n // #3\n tail.elements[writeIndex] = handle;\n handle.stack = null;\n // #4\n tail.lazySet(writeIndex + 1);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1","attrs":{}}],"attrs":{}},{"type":"text","text":" 控制回收頻率,避免WeakOrderQueue增長過快。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每8個對象都會拋棄7個,回收一個","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2","attrs":{}}],"attrs":{}},{"type":"text","text":" 當前Link#elements已全部使用,創建一個新的Link","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3","attrs":{}}],"attrs":{}},{"type":"text","text":" 存入緩存對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#4","attrs":{}}],"attrs":{}},{"type":"text","text":" 延遲設置Link#elements的最新索引(Link繼承了AtomicInteger),這樣在該stack主線程通過該索引獲取elements緩存對象時,保證elements中元素已經可見。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"獲取對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Recycler#threadLocal中存放了每個線程對應的Stack。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Recycler#get中首先獲取屬於當前線程的Stack,再從該Stack中獲取對象,也就是,每個線程只能從自己的Stack中獲取對象。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Recycler#get -> Stack#pop","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"DefaultHandle pop() {\n int size = this.size;\n if (size == 0) {\n // #1\n if (!scavenge()) {\n return null;\n }\n size = this.size;\n if (size <= 0) {\n return null;\n }\n }\n // #2\n size --;\n DefaultHandle ret = elements[size];\n elements[size] = null;\n this.size = size;\n\n ...\n return ret;\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1","attrs":{}}],"attrs":{}},{"type":"text","text":" elements沒有可用對象時,將WeakOrderQueue中的對象遷移到elements","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2","attrs":{}}],"attrs":{}},{"type":"text","text":" 從elements中取出一個緩存對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"scavenge -> scavengeSome -> WeakOrderQueue#transfer","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"boolean transfer(Stack> dst) {\n Link head = this.head.link;\n if (head == null) {\n return false;\n }\n // #1\n if (head.readIndex == LINK_CAPACITY) {\n if (head.next == null) {\n return false;\n }\n head = head.next;\n this.head.relink(head);\n }\n // #2\n final int srcStart = head.readIndex;\n int srcEnd = head.get();\n final int srcSize = srcEnd - srcStart;\n if (srcSize == 0) {\n return false;\n }\n // #3\n final int dstSize = dst.size;\n final int expectedCapacity = dstSize + srcSize;\n\n if (expectedCapacity > dst.elements.length) {\n final int actualCapacity = dst.increaseCapacity(expectedCapacity);\n srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);\n }\n\n if (srcStart != srcEnd) {\n final DefaultHandle[] srcElems = head.elements;\n final DefaultHandle[] dstElems = dst.elements;\n int newDstSize = dstSize;\n // #4\n for (int i = srcStart; i < srcEnd; i++) {\n DefaultHandle> element = srcElems[i];\n ...\n srcElems[i] = null;\n // #5\n if (dst.dropHandle(element)) {\n continue;\n }\n element.stack = dst;\n dstElems[newDstSize ++] = element;\n }\n // #6\n if (srcEnd == LINK_CAPACITY && head.next != null) {\n this.head.relink(head.next);\n }\n\n head.readIndex = srcEnd;\n // #7\n if (dst.size == newDstSize) {\n return false;\n }\n dst.size = newDstSize;\n return true;\n } else {\n // The destination stack is full already.\n return false;\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就是把WeakOrderQueue中的對象遷移到Stack中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1","attrs":{}}],"attrs":{}},{"type":"text","text":" head.readIndex 標誌現在已遷移對象下標","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"head.readIndex == LINK_CAPACITY","attrs":{}}],"attrs":{}},{"type":"text","text":",表示當前Link已全部移動,查找下一個Link","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2","attrs":{}}],"attrs":{}},{"type":"text","text":" 計算待遷移對象數量","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,Link繼承了AtomicInteger","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3","attrs":{}}],"attrs":{}},{"type":"text","text":" 計算Stack#elements數組長度,不夠則擴容","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#4","attrs":{}}],"attrs":{}},{"type":"text","text":" 遍歷待遷移的對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#5","attrs":{}}],"attrs":{}},{"type":"text","text":" 控制回收頻率","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#6","attrs":{}}],"attrs":{}},{"type":"text","text":" 當前Link對象已全部移動,修改WeakOrderQueue#head的link屬性,指向下一Link,這樣前面的Link就可以被垃圾回收了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#7","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dst.size == newDstSize","attrs":{}}],"attrs":{}},{"type":"text","text":" 表示並沒有對象移動,返回false","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"否則更新dst.size","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實對象池的實現難點在於線程安全。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Recycler中將主線程和非主線程回收對象劃分到不同的存儲空間中(stack#elements和WeakOrderQueue.Link#elements),並且對於WeakOrderQueue.Link#elements,存取操作劃分到兩端進行(非主線程從尾端存入,主線程從首部開始讀取),","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從而減少同步操作,並保證線程安全。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,Netty還提供了更高級別的對象池類ObjectPool,使用方法可以參考PooledDirectByteBuf#RECYCLER,這裏不再贅述。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果您覺得本文不錯,歡迎關注我的微信公衆號,系列文章持續更新中。您的關注是我堅持的動力!","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5c/5cb935abd5751b075beefdc1bf4914a5.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章