Nodejs經過Thrift操做hbase卡住緣由分析及與javascript的垃圾回收機制的關係

在最近使用Nodejs經過Thrift操做hbase的時候寫了個腳本,不斷髮送http請求,從而取得hbase下所需的數據,可是在run的過程當中for循環並無執行徹底,在執行一部分後會卡住,就再也進不到hbase下取數據,出現socket hang up的錯誤,查了不少資料也沒解決。當時認爲是hbase的併發數問題,其併發數的限制致使了資源負載的極限,後來不斷測試找到緣由所在,其實與hbase處理併發的能力無關,真正的緣由是jsvascript的垃圾回收機制使得資源使用達到瓶頸,下面是代碼處理前與處理後的對比:javascript

 1 var thrift = require('thrift'),
 2     HBase = require('./gen-nodejs/Hbase.js'),
 3     HBaseTypes = require('./gen-nodejs/Hbase_types.js'),
 4     connection = thrift.createConnection('localhost', 9090, {
 5         transport: thrift.TFramedTransport,
 6         protocol: thrift.TBinaryProtocol
 7     });
 8 
 9 var client = thrift.createClient(HBase,connection);
10 
11 /*router.get('/', function(req, res)
12 {
13 
14   res.render('index', { title: 'Welcome' });
15 
16 });*/
17 
18 router.get('/search', function(req, res)
19 {
20 
21     var dateTimeArray = {};
22     var valueArray = {};
23     var searchPlateBegin = null;
24     var searchPlateEnd = null;
25     var searchDetailsBegin = null;
26     var searchDetailsEnd = null;
27     var convertReverseArray = new Array();
28 }
 1 /*router.get('/', function(req, res)
 2 {
 3 
 4   res.render('index', { title: 'Welcome' });
 5 
 6 });*/
 7 
 8 router.get('/search', function(req, res)
 9 {
10 
11     var dateTimeArray = {};
12     var valueArray = {};
13     var searchPlateBegin = null;
14     var searchPlateEnd = null;
15     var searchDetailsBegin = null;
16     var searchDetailsEnd = null;
17     var convertReverseArray = new Array();
18 
19      var thrift = require('thrift'),
20     HBase = require('./gen-nodejs/Hbase.js'),
21     HBaseTypes = require('./gen-nodejs/Hbase_types.js'),
22     connection = thrift.createConnection('localhost', 9090, {
23         transport: thrift.TFramedTransport,
24         protocol: thrift.TBinaryProtocol
25     });
26 
27 var client = thrift.createClient(HBase,connection);
28 }

 兩段代碼的惟一區別就是thrift鏈接hbase的幾行代碼是否放在了路由search下,在運行的腳本之中不斷地請求該路由這就是緣由所在,因爲不斷請求,每請求一次都會進行一次thrift鏈接hbase,那段代碼看似很簡單,其實背後的運行很複雜,包括hbase中庫的添加,結構組織,內存的分配等等,這樣循環下去就建立了很是多的thrift對象,因爲JavaScript垃圾回收機制的延時性不可能都進行回收,這樣方法對象的不斷增多就會形成thrift從hbase中請求數據的阻塞,就像咱們看到的那樣,卡在了那裏。java

下面來講一下javascript的回收機制:node

如今各大瀏覽器一般用採用的垃圾回收有兩種方法:標記清除、引用計數。數組

1.標記清除:瀏覽器

這是JavaScript最多見的垃圾回收方式,當變量進入執行環境的時候,好比函數中聲明一個變量,垃圾回收器將其標記爲「進入環境」,當變量離開環境的時候(函數執行結束)將其標記爲「離開環境」。至於怎麼標記有不少種方式,好比特殊位的反轉、維護一個列表等,這些並不重要,重要的是使用什麼策略,原則上講不可以釋放進入環境的變量所佔的內存,它們隨時可能會被調用的到。閉包

垃圾回收器會在運行的時候給存儲在內存中的全部變量加上標記,而後去掉環境中的變量以及被環境中變量所引用的變量(閉包),在這些完成以後仍存在標記的就是要刪除的變量了,由於環境中的變量已經沒法訪問到這些變量了,而後垃圾回收器相會這些帶有標記的變量機器所佔空間。併發

2.引用計數:socket

另外一種不太常見的垃圾回收策略是引用計數。引用計數的含義是跟蹤記錄每一個值被引用的次數。當聲明瞭一個變量並將一個引用類型賦值給該變量時,則這個值的引用次數就是1。相反,若是包含對這個值引用的變量又取得了另一個值,則這個值的引用次數就減1。當這個引用次數變成0時,則說明沒有辦法再訪問這個值了,於是就能夠將其所佔的內存空間給收回來。這樣,垃圾收集器下次再運行時,它就會釋放那些引用次數爲0的值所佔的內存。函數

好比對象A有一個屬性指向對象B,而對象B也有有一個屬性指向對象A,這樣相互引用測試

function test(){
            var a={};
            var b={};
            a.prop=b;
            b.prop=a;
        }

這樣a和b的引用次數都是2,即便在test()執行完成後,兩個對象都已經離開環境,在標記清除的策略下是沒有問題的,離開環境的就被清除,可是在引用計數策略下不行,由於這兩個對象的引用次數仍然是2,不會變成0,因此其佔用空間不會被清理,若是這個函數被屢次調用,這樣就會不斷地有空間不會被回收,形成內存泄露。

減小JavaScript中的垃圾回收:

1.數組Array優化:

將[]賦值給一個數組對象,是清空數組的捷徑(例如: arr = [];),可是須要注意的是,這種方式又建立了一個新的空對象,而且將原來的數組對象變成了一小片內存垃圾!實際上,將數組長度賦值爲0(arr.length = 0)也能達到清空數組的目的,而且同時能實現數組重用,減小內存垃圾的產生。

2.函數function優化:

方法通常都是在初始化的時候建立,而且此後不多在運行時進行動態內存分配,這就使得致使內存垃圾產生的方法,找起來就不是那麼容易了。可是從另外一角度來講,這更便於咱們尋找了,由於只要是動態建立方法的地方,就有可能產生內存垃圾。

setTimeout(
    (function(self) {                    
      return function () {
              self.tick();
    };
})(this), 16)

每過16毫秒調用一次this.tick(),嗯,乍一看彷佛沒什麼問題,可是仔細一琢磨,每一次調用都返回了一個新的方法對象,這就致使了大量的方法對象垃圾!

能夠將做爲返回值的方法保存起來,例如:

this.tickFunc = (
    function(self) {
      return function() {
                self.tick();
      };
    }
)(this);

// in the tick() function
setTimeout(this.tickFunc, 16);

相比於每次都新建一個方法對象,這種方式在每一幀當中重用了相同的方法對象。這種方式的優點是顯而易見的,而這種思想也能夠應用在任何以方法爲返回值或者在運行時建立方法的狀況當中。

相關文章
相關標籤/搜索