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);

相比于每次都新建一个方法对象,这种方式在每一帧当中重用了相同的方法对象。这种方式的优点是显而易见的,而这种思想也能够应用在任何以方法为返回值或者在运行时建立方法的状况当中。

相关文章
相关标签/搜索