jQuery / Sizzle checkContext内存泄漏

时间:2013-06-24 15:23:08

标签: javascript jquery dom memory-leaks google-chrome-devtools

在DevTools中使用'Profiles'调试我的应用程序时,我发现“Detached DOM tree”正在累积。这些分离的节点具有保留树,主要包含checkContext个函数(来自sizzle内的jQuery - v1.10.1)。

Heap snapshot

我不知道如何处理这个问题。这个结果意味着什么?

2 个答案:

答案 0 :(得分:5)

Sizzle将已编译的选择器存储在选择器缓存中,默认情况下最多可存储50个条目。您可以在进行任何选择之前设置$.expr.cacheLength = 1进行试验,看看它们是否会消失。

这是文档https://github.com/jquery/sizzle/wiki/Sizzle-Documentation#-internal-api。看似内部,所以不要依赖它或实际生产代码中的任何东西。

答案 1 :(得分:3)

这实际上是一个错误,Sizzle没有理由需要挂在上下文节点上,它只是这样做,因为它在设置临时变量后没有清理。我提交了issue for it,修复了它,运行了所有的Sizzle测试,然后做了拉取请求。

如果你想修补现有的jQuery或Sizzle副本:

  1. 打开您的jQuery或Sizzle文件

  2. 搜索matcherFromTokens功能

  3. 在其中找到此代码(靠近顶部):

    matchers = [ function( elem, context, xml ) {
        return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
            (checkContext = context).nodeType ?
                matchContext( elem, context, xml ) :
                matchAnyContext( elem, context, xml ) );
    } ];
    
  4. return更改为var rv =,然后在匿名函数的末尾添加checkContext = undefined;然后再添加return rv;,例如:

    matchers = [ function( elem, context, xml ) {
        var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
            (checkContext = context).nodeType ?
                matchContext( elem, context, xml ) :
                matchAnyContext( elem, context, xml ) );
        // Release the context node (issue #299)
        checkContext = null;
        return ret;
    } ];
    
  5. 注意:该代码会将null分配给checkContext,因为这显然是他们的风格。如果是我,我已经分配了undefined

    如果在拉取请求/合并过程中出现修复问题,我会更新答案。

    继续使用Sizzle缓存选择器会更好,因为jQuery使用已编译的选择器和事件委托,并且您不希望每次相关时都必须重新分析和重建匹配器函数事件发生,所以它可以判断元素是否匹配它。


    遗憾的是,这并不是jQuery在编译选择器中保留元素的唯一地方。它所做的每个地方都可能是一个可以使用修复的bug。我只有时间跟踪另一个,我也报告并修复了(待拉动请求到位):

    如果您搜索"潜在复杂的伪" ,您会在:not伪选择器中找到它:

    pseudos: {
        // Potentially complex pseudos
        "not": markFunction(function( selector ) {
            // Trim the selector passed to compile
            // to avoid treating leading and trailing
            // spaces as combinators
            var input = [],
                results = [],
                matcher = compile( selector.replace( rtrim, "$1" ) );
    
            return matcher[ expando ] ?
                markFunction(function( seed, matches, context, xml ) {
                    var elem,
                        unmatched = matcher( seed, null, xml, [] ),
                        i = seed.length;
    
                    // Match elements unmatched by `matcher`
                    while ( i-- ) {
                        if ( (elem = unmatched[i]) ) {
                            seed[i] = !(matches[i] = elem);
                        }
                    }
                }) :
                function( elem, context, xml ) {
                    input[0] = elem;
                    matcher( input, null, xml, results );
                    return !results.pop();
                };
        }),
    

    问题出在条件运算符{/ 1}}之后的函数中:

    :

    请注意,它永远不会清除function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); return !results.pop(); }; 。这是修复:

    input[0]

    我现在有时间跟踪它。