摘要:對象池類的成員應該都是靜態的。事實上,由于對象池技術將對象限制在一定的數量,也有效地減少了應用程序內存上的開銷。對生成時開銷不大的對象進行池化,反而可能會出現維護對象池的開銷大于生成新對象的開銷,從而使性能降低的情況。
前言
在學習 React 事件系統的時候,在事件分發的 dispatch方法發現了調用了一個 pooledClass 方法,一時半會沒看明白這個方法的用意。
我們先看一下是怎么用的:
// step1 function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) { this.topLevelType = topLevelType; this.nativeEvent = nativeEvent; this.ancestors = []; } Object.assign(TopLevelCallbackBookKeeping.prototype, { destructor: function() { this.topLevelType = null; this.nativeEvent = null; this.ancestors.length = 0; }, }); PooledClass.addPoolingTo( TopLevelCallbackBookKeeping, PooledClass.twoArgumentPooler ); // step2 var bookKeeping = TopLevelCallbackBookKeeping.getPooled( topLevelType, nativeEvent ); // bookKeeping 是 TopLevelCallbackBookKeeping 的實例 try { // Event queue being processed in the same cycle allows // `preventDefault`. ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping); } finally { //釋放 TopLevelCallbackBookKeeping.release(bookKeeping); }
那么這里為什么不直接 new 一個 TopLevelCallbackBookKeeping, 而要通過這個 PooledClass 來返回 TopLevelCallbackBookKeeping 的實例呢
對象池單例模式是限制了一個類只能有一個實例,對象池模式則是限制一個類實例的個數。對象池類就像是一個對象管理員,它以Static列表(也就是裝對象的池子)的形式存存儲某個實例數受限的類的實例,每一個實例還要加一個標記,標記該實例是否被占用。當類初始化的時候,這個對象池就被初始化了,實例就被創建出來。然后,用戶可以向這個類索取實例,如果池中所有的實例都已經被占用了,那么拋出異常。用戶用完以后,還要把實例“還”回來,即釋放占用。對象池類的成員應該都是靜態的。用戶也不應該能訪問池子里裝著的對象的構造函數,以防用戶繞開對象池創建實例。書上說這個模式會用在數據庫連接的管理上。比如,每個用戶的連接數是有限的,這樣每個連接就是一個池子里的一個對象,“連接池”類就可以控制連接數了。對象池技術的基本原理
如果說每次觸發 dispatch 的時候都用 new TopLevelCallbackBookKeeping 來 new 一個對象,那么當觸發很多次 dispatch 的時候,就會導致生成多個對象無法銷毀(多個bookKeeping的引用次數一直為1),導致內存溢出。
對象池技術基本原理的核心有兩點:緩存和共享,即對于那些被頻繁使用的對象,在使用完后,不立即將它們釋放,而是將它們緩存起來,以供后續的應用程序重復使用,從而減少創建對象和釋放對象的次數,進而改善應用程序的性能。事實上,由于對象池技術將對象限制在一定的數量,也有效地減少了應用程序內存上的開銷。
對象池使用的基本思路是將用過的對象保存起來,等下一次需要這種對象的時候,再拿出來重復使用,從而在一定程度上減少頻繁創建對象所造成的開銷。React 的 pooledClass.js 就是一個例子:
var invariant = require("invariant"); /** * Static poolers. Several custom versions for each potential number of * arguments. A completely generic pooler is easy to implement, but would * require accessing the `arguments` object. In each of these, `this` refers to * the Class itself, not an instance. If any others are needed, simply add them * here, or in their own files. */ var oneArgumentPooler = function(copyFieldsFrom) { var Klass = this; if (Klass.instancePool.length) { var instance = Klass.instancePool.pop(); Klass.call(instance, copyFieldsFrom); return instance; } else { return new Klass(copyFieldsFrom); } }; ... var standardReleaser = function(instance) { var Klass = this; invariant( instance instanceof Klass, "Trying to release an instance into a pool of a different type." ); instance.destructor(); if (Klass.instancePool.length < Klass.poolSize) { Klass.instancePool.push(instance); } }; var DEFAULT_POOL_SIZE = 10; var DEFAULT_POOLER = oneArgumentPooler; /** * Augments `CopyConstructor` to be a poolable class, augmenting only the class * itself (statically) not adding any prototypical fields. Any CopyConstructor * you give this may have a `poolSize` property, and will look for a * prototypical `destructor` on instances (optional). * * @param {Function} CopyConstructor Constructor that can be used to reset. * @param {Function} pooler Customizable pooler. */ var addPoolingTo = function(CopyConstructor, pooler) { var NewKlass = CopyConstructor; NewKlass.instancePool = []; NewKlass.getPooled = pooler || DEFAULT_POOLER; if (!NewKlass.poolSize) { NewKlass.poolSize = DEFAULT_POOL_SIZE; } NewKlass.release = standardReleaser; return NewKlass; }; var PooledClass = { addPoolingTo: addPoolingTo, oneArgumentPooler: oneArgumentPooler, twoArgumentPooler: twoArgumentPooler, threeArgumentPooler: threeArgumentPooler, fourArgumentPooler: fourArgumentPooler, fiveArgumentPooler: fiveArgumentPooler, }; module.exports = PooledClass;
具體分為三步
addPoolingTo 添加對象到池子
調用的時候發現是否有緩存,有緩存就pop()出來用, 沒有緩存就新增一個
使用完成之后,釋放對象,緩存進去
說的再簡單一點就是
創建對象 addPoolingTo()
借取對象 getPooled()
歸還對象 release()
總結并非所有對象都適合拿來池化――因為維護對象池也要造成一定開銷。對生成時開銷不大的對象進行池化,反而可能會出現“維護對象池的開銷”大于“生成新對象的開銷”,從而使性能降低的情況。但是對于生成時開銷可觀的對象,池化技術就是提高性能的有效策略了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/97921.html
摘要:在項目架構中這兩個東西基本成為了標配,但的模塊必須在使用前經過的構建后文稱為才能在瀏覽器端使用,而每次修改也都需要重新構建后文稱為才能生效,如何提高的構建效率成為了提高開發效率的關鍵之一。 0. 前言 showImg(https://segmentfault.com/img/remote/1460000005770045); 圖1:ES6 + Webpack + React + Bab...
摘要:本篇開始介紹自定義組件是如何渲染的。組件將自定義組件命名為,結構如下經過編譯后,生成如下代碼構建頂層包裝組件跟普通元素渲染一樣,第一步先會執行創建為的。調用順序已在代碼中注釋。先看圖,這部分內容將在下回分解 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非...
摘要:在學習源碼的過程中,給我幫助最大的就是這個系列文章,于是決定基于這個系列文章談一下自己的理解。到此為止,首次渲染就完成啦總結從啟動到元素渲染到頁面,并不像看起來這么簡單,中間經歷了復雜的層級調用。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非常艱辛的過...
摘要:依賴注入和控制反轉,這兩個詞經常一起出現。一句話表述他們之間的關系依賴注入是控制反轉的一種實現方式。而兩者有大量的代碼都是可以共享的,這就是依賴注入的使用場景了。下一步就是創建具體的依賴內容,然后注入到需要的地方這里的等于這個對象。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級...
摘要:調用棧是這樣的這里生成的我們將其命名為,它將作為參數傳入到。整個的調用棧是這樣的組件間的層級結構是這樣的到此為止,頂層對象已經構造完畢,下一步就是調用來自的方法,進行頁面的渲染了。通過表達的結構最終會轉化為一個純對象,用于下一步的渲染。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言...
閱讀 2953·2023-04-26 01:32
閱讀 1548·2021-09-13 10:37
閱讀 2286·2019-08-30 15:56
閱讀 1678·2019-08-30 14:00
閱讀 3052·2019-08-30 12:44
閱讀 1968·2019-08-26 12:20
閱讀 1067·2019-08-23 16:29
閱讀 3233·2019-08-23 14:44