本文介绍了在Javascript中减少垃圾收集器活动的最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个相当复杂的Javascript应用程序,它有一个主循环,每秒被调用60次。似乎有很多垃圾回收正在进行(基于Chrome开发工具中内存时间线的锯齿输出) - 这通常会影响应用程序的性能。



因此,我试图研究减少垃圾收集器必须完成的工作量的最佳实践。 (我在网上找到的大部分信息都是关于避免内存泄漏的,这是一个稍微不同的问题 - 我的内存正在被释放,只是垃圾收集太多了。)我假设这主要归结为尽可能多地使用对象,但当然魔鬼是在细节。



该应用程序是按照。



我认为一个问题是某些函数每秒可能被调用数千次(因为它们在主循环的每次迭代中使用了数百次),并且这些函数(字符串,数组等)中的局部工作变量可能是这个问题。

我知道大/较大对象的对象池(我们使用这个程度),但我正在寻找可以适用于b oard,尤其是与在紧密循环中被多次调用的函数有关。

我可以使用哪些技术来减少垃圾收集器必须的工作量做什么?

或许还有 - 可以采用哪些技术来确定哪些对象最被垃圾收集? (这是一个非常大的代码库,因此比较堆的快照并不是非常有效)。 解决方案

很多事情你需要做的是尽量减少GC流失,在大多数其他情况下与所考虑的惯用JS相反,因此请在判断我给出的建议时记住上下文。



分配发生在几个地方的现代解释器中:


  1. 当您通过 new 创建对象或通过文字语法 [...] {}

  2. 当您连接字符串时。

  3. 当您输入包含函数声明的作用域时。
  4. >
  5. 当您评估函数表达式时:(function(...){...})

  6. 当您执行一个强制对象的操作时,如 Object(myNumber) Number.prototype.toString.call(42)

  7. 当你调用一个内置的引擎时,像 Array.prototype.slice

  8. 当你使用参数以反映参数列表。

  9. 分割字符串或与正则表达式匹配时。

避免执行这些操作,尽可能汇集并重用对象。



特别注意寻找机会到:


  1. 将内部函数在关闭状态下没有或很少依赖关系拉入更长寿命的范围。 (像这样的代码缩小器可以内联内部函数,并可能改进GC性能。)

  2. 避免使用字符串来表示结构化数据或动态寻址。特别是避免使用 split 或正则表达式匹配进行反复解析,因为每个对象都需要多个对象分配。这经常发生在查找表和动态DOM节点ID的键中。例如, lookupTable ['foo-'+ x] document.getElementById('foo-'+ x)由于存在字符串连接,因此都涉及分配。通常,您可以将键连接到长寿命对象而不是重新连接。根据您需要支持的浏览器,您可以使用直接使用对象作为键。

  3. 避免捕获正常代码路径上的异常。而不是 try {op(x)} catch(e){...} ,do if(!opCouldFailOn(x)){op X); } else {...}

  4. 当您无法避免创建字符串时,例如要将消息传递给服务器,请使用像 JSON.stringify 这样的内置命令,它使用内部本机缓冲区来累积内容,而不是分配多个对象。

  5. 避免对高频事件使用回调函数,并在可能的位置传递一个长期生效的函数(请参阅1),以便从消息内容中重新创建状态。

  6. 避免使用参数,因为函数调用时必须创建一个类似数组的对象。 $ b

    我建议使用 JSON.stringify 创建传出网络消息。使用 JSON.parse 解析输入消息显然涉及分配,大量消息用于大型消息。如果您可以将传入消息表示为基元数组,则可以节省大量分配。您可以构建一个不分配的解析器的唯一其他内置函数是 String.prototype.charCodeAt 。一个复杂格式的解析器,只有使用它才会被阅读。


    I have a fairly complex Javascript app, which has a main loop that is called 60 times per second. There seems to be a lot of garbage collection going on (based on the 'sawtooth' output from the Memory timeline in the Chrome dev tools) - and this often impacts the performance of the application.

    So, I'm trying to research best practices for reducing the amount of work that the garbage collector has to do. (Most of the information I've been able to find on the web regards avoiding memory leaks, which is a slightly different question - my memory is getting freed up, it's just that there's too much garbage collection going on.) I'm assuming that this mostly comes down to reusing objects as much as possible, but of course the devil is in the details.

    The app is structured in 'classes' along the lines of John Resig's Simple JavaScript Inheritance.

    I think one issue is that some functions can be called thousands of times per second (as they are used hundreds of times during each iteration of the main loop), and perhaps the local working variables in these functions (strings, arrays, etc.) might be the issue.

    I'm aware of object pooling for larger/heavier objects (and we use this to a degree), but I'm looking for techniques that can be applied across the board, especially relating to functions that are called very many times in tight loops.

    What techniques can I use to reduce the amount of work that the garbage collector must do?

    And, perhaps also - what techniques can be employed to identify which objects are being garbage collected the most? (It's a farly large codebase, so comparing snapshots of the heap has not been very fruitful)

    解决方案

    A lot of the things you need to do to minimize GC churn go against what is considered idiomatic JS in most other scenarios, so please keep in mind the context when judging the advice I give.

    Allocation happens in modern interpreters in several places:

    1. When you create an object via new or via literal syntax [...], or {}.
    2. When you concatenate strings.
    3. When you enter a scope that contains function declarations.
    4. When you perform an action that triggers an exception.
    5. When you evaluate a function expression: (function (...) { ... }).
    6. When you perform an operation that coerces to Object like Object(myNumber) or Number.prototype.toString.call(42)
    7. When you call a builtin that does any of these under the hood, like Array.prototype.slice.
    8. When you use arguments to reflect over the parameter list.
    9. When you split a string or match with a regular expression.

    Avoid doing those, and pool and reuse objects where possible.

    Specifically, look out for opportunities to:

    1. Pull inner functions that have no or few dependencies on closed-over state out into a higher, longer-lived scope. (Some code minifiers like Closure compiler can inline inner functions and might improve your GC performance.)
    2. Avoid using strings to represent structured data or for dynamic addressing. Especially avoid repeatedly parsing using split or regular expression matches since each requires multiple object allocations. This frequently happens with keys into lookup tables and dynamic DOM node IDs. For example, lookupTable['foo-' + x] and document.getElementById('foo-' + x) both involve an allocation since there is a string concatenation. Often you can attach keys to long-lived objects instead of re-concatenating. Depending on the browsers you need to support, you might be able to use Map to use objects as keys directly.
    3. Avoid catching exceptions on normal code-paths. Instead of try { op(x) } catch (e) { ... }, do if (!opCouldFailOn(x)) { op(x); } else { ... }.
    4. When you can't avoid creating strings, e.g. to pass a message to a server, use a builtin like JSON.stringify which uses an internal native buffer to accumulate content instead of allocating multiple objects.
    5. Avoid using callbacks for high-frequency events, and where you can, pass as a callback a long-lived function (see 1) that recreates state from the message content.
    6. Avoid using arguments since functions that use that have to create an array-like object when called.

    I suggested using JSON.stringify to create outgoing network messages. Parsing input messages using JSON.parse obviously involves allocation, and lots of it for large messages. If you can represent your incoming messages as arrays of primitives, then you can save a lot of allocations. The only other builtin around which you can build a parser that does not allocate is String.prototype.charCodeAt. A parser for a complex format that only uses that is going to be hellish to read though.

    这篇关于在Javascript中减少垃圾收集器活动的最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-18 06:48