簡單內存複用與細節優化

一、內存優化細節

如圖可以看出有內存抖動,解決辦法是內存緩存。

使用內存緩存的時候需要注意的點就是當一個實例不再使用的時候,也應該去釋放這個實例,不能讓這個實例越積累越多,從而導致內存溢出。所以內存溢出的概念就是,當一個實例被創建之後,後續過程中不可能用到這個實例的時候,但是我們把這份實例給存儲起來了,導致GC無法回收這部分實例,也就導致內存不斷上漲,這就是內存溢出。內存溢出是一個不斷上漲的曲線,並且GC回收後該曲線也無法下降。

但是這個曲線並不是代表內存溢出,而是生產者的速度大於消費者的速度,此時我們緩衝區中的數據會不斷上升,達到無限狀態,將內存佔用完。

register方法中就很有可能造成內存抖動,IoTask創建之後沒有進行復用,每次都會去創建一個,創建之後就沒有了,用掉之後就丟掉了,

 

雖然在執行完成之後,如果返回true,會重新加載到隊列當中,此時有一個內存複用的優化,所以對性能的損耗不會太多。但是應該避免出現這樣的情況,因爲有多個線程調用,此外這個方法可能返回的是一個false,沒有返回true。所以這裏需要優化。

register方法傳入了一個SocketChannel、一個類型、一個callback,callback用於真正的任務執行的時候任務的調度,這個方法可以通過IoStealingSelectorProvider來看到。在這個方法中可以看到調用了callback的run方法,

 

由於callback的run方法返回的是void

 

這也就導致了provider無法返回true直接進行註冊的原因,也就是說無法直到當前這個程序執行這一遍數據之後,它後續還有沒有數據。

 

它只能通過後續重新註冊的方法,也就是再次進行register的方法,來進行任務的註冊

而不能直接通過做完任務的方法直接進行註冊。所以,應該將IoTask和Io'Provider聯繫起來

 

IoProvider中的HandlerCallback,本質來說它就是一個執行任務的地方,IoTask也是,可以將這兩者進行一個聯合。

IoTask增加一個返回boolean值的onProcessIo的方法,表明是否處於Io執行的狀態。

 

在IoProvider的callback中,之前onProviderIo返回的是void。

在run方法中,首先清空attach,如果onProviderIo(attach)返回true,說明還有數據需要處理,再重新註冊。

二、總結

 

三、內存抖動優化

Register是我們認爲會產生內存抖動的地方,爲什麼會這麼認爲呢?因爲每次調用register,都會執行new Task創建一個新的實例的方法,而這個實例在後面的流程當中,用完了也就丟掉了,並沒有把它持續地循環利用。

在讀取和寫入的時候都會調用這個實例,而讀取和寫入的調度,其實是在整個生命週期當中,當有數據發生一個讀取或者寫入之後,它都會進行一個判斷,而整個業務流程的Dispatcher都會對它進行註冊。整個調度過程由selector的線程來完成,這個線程就是Loop Thread。這個Loop Thread本身就在循環,本身會產生非常多的讀取或寫入的註冊事件,這個註冊事件會導致register頻繁地被觸發,頻繁地register,也就會導致頻繁地創建Task,而這N個Task隨後用完就丟,內存不斷上漲,漲到一定程度被GC回收,內存回收會引起內存波浪型地抖動。鋸齒的形狀從側面反映了GC的頻繁度,GC越頻繁,就導致了其它線程不工作的狀態,也就降低了性能。

 

優化方式:

1、

之前processTask這個地方永遠返回的是false,此時將其做了一個具體的實現,根據業務的實現返回true或者false。如果返回true,直接將其添加到隊列中去。

這裏主要完成了兩個問題,一個是task的複用,第二個是減少了register的調度,這就是我們的優化點。

 

2、register對其進行了更改,之前是需要傳入通道、類型信息和callback,但是經過排查和分析之後,發現task其實是可以和callback合併在一起的。既然可以合併在一起,那麼傳入的時候就可以直接傳入task,從而降低new task這個過程。而task內部則包括了channel和類型,callback是由Task的直接子類來完成。

 

3、

Task的直接子類在Adapter的實現中來完成,在adapter中本身就有兩個基礎的變量,這兩個變量就是我們的callback,分別代表了輸入和輸出,callback是IoTask的子類,也就是說當前的IoTask是隨着當的adapter實例一起創建的,當adapter的實例在構造方法創建的時候,在內部就完成了對應task的實現。簡單來說,一個連接只可能創建兩個task,並且這兩個連接沒有關閉,那麼這兩個連接將會永遠去複用這個task,不會再創建新的task的實例。這裏就保證了task上漲之後不再使用的問題,從而減少了IoTask引起的內存抖動。當然,在整個代碼中不是隻有這一個地方會引起內存抖動,還有其它非常多的地方,比如IoArgs。IoArgs已經固化到了Selector當中,這個點其實是可以忽略的。第二點是Frame,幀的構建本身就是可能會引起內存抖動的地方,以及我們的packet,這個地方是內存抖動的大頭,這些都是可以進行優化的地方。

 

 

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