美文网首页
记一次性能优化

记一次性能优化

作者: iscaster | 来源:发表于2017-02-19 23:53 被阅读0次

    有那么一句名言,过早优化是万恶之源。指的就是在开发过程中,不用太早考虑性能问题,而是要优先实现功能和保持代码清晰,简洁。更深的原因,是对个人的判断的极端不信任。我们觉得重要的往往不重要,我们觉得不重要的事后才发现很重要。事前的判断,往往会错得离谱。所以,在软件工程中,就放弃了事前对可能的性能占用情况的评估,而是在实现运行中,通过软件工具,来分析性能瓶颈,即:profiling.

    我最近就遇到一个profiling的情况。

    最初是因为有那么一个bug。导致js后台处理进程,一小时左右,会自动退出。经过分析,我发现这是我所使用的kafka连接库产生的问题,这个问题不好修复,但是我可以使用trick绕过它。处理之后,进程不会报错退出了,但是,在运行3-4小时后,进程的处理性能会出现巨大的下降,而且内存占用会不停的增长,不停的刷新占用的上限。

    很明显,这个js进程存在内存泄漏的情况。

    但是内存泄漏是哪里产生的咧?

    由于之前没有做过profiling,无法判断是旧有的代码就有问题,还是新加入的代码导致的。所以只能全局的去考虑。

    最开始,我使用了nodejs自带的profiling工具进行过一些cpu性能分析

    https://nodejs.org/en/docs/guides/simple-profiling/

    后来我又找到了更直观的node-inspector

    https://github.com/node-inspector/node-inspector

    通过node-inspector分析,所能了解的也不多。只是看到closure,也就是闭包,有占到26%的内存,是最大的一项。

    这么说,是我代码中的closure导致的内存泄漏。我又搜索了相关资料,了解了一下closure导致内存泄漏的常见形式。给我启发最大的是这么一篇:

    http://point.davidglasser.net/2013/06/27/surprising-javascript-memory-leak.html

    在里面提到js v8一个惊人的细节。在同一作用域定义的闭包,会共享上下文(context),经典的触发代码如下:

    var str = new Array(1000000).join('*');

    var doSomethingWithStr = function () {

      if (str === 'something')

        console.log("str was something");

      };

      doSomethingWithStr();

      var logIt = function () {

        console.log('interval');

      }

      setInterval(logIt, 100);

    };

    setInterval(run, 1000);

    在这段代码中logIt和doSomethingWithStr共享上下文,引用了str,导致str不被会gc清理,从而内存泄漏。

    我又看了一下代码,代码中并没有使用setInteval函数,但使用的Rxjs,Rxjs的Observable.subscribe函数会不会有和setInteval函数类似的效果。我立即写了段代码测试了一下。

    var subscription = null

    var sequence = null

    function run() {

      var str = new Array(1000000).join('*');

      if (subscription != null) {

        subscription.dispose()

      }

    var sequence = Rx.Observable.interval(200)

    var subscription = sequence.subscribe(

      function() {

        console.log("str[0]=", str[0])

      })

    }

    setInterval(run, 500);

    使用工具查看,发现这段代码会导致内存占用迅速上升,即存在内存泄漏。

    经过试验后,我找到了初步的解决方案。

    var subscription = null

    var sequence = null

    function run() {

      var str = new Array(1000000).join('*');

      if (subscription != null) {

        subscription.dispose()

      }

      var sequence = Rx.Observable.interval(200).take(3)

      var subscription = sequence.subscribe(

      function() {

        console.log("str[0]=", str[0])

      } , function(err) {

        console.log("err=", err)

      } , function(err) {

        str=null

        console.log("completed")

      } )

    }

    setInterval(run, 500);

    即在消息流结束的时候,将str对象置为null,来防止str对象的泄漏。本以为这就是最佳方案。无意中我又发现,哪怕没有在onCompleted函数,只要流结束,也不会内存泄漏。

    我觉得这个rxjs的一个bug。在dispose后,不就是应该该把要清理都清理了,是吧?

    相关文章

      网友评论

          本文标题:记一次性能优化

          本文链接:https://www.haomeiwen.com/subject/obhzittx.html