jQuery / Sizzle checkContext内存泄漏

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

堆快照

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

这实际上是一个错误,没有理由Sizzle需要挂在上下文节点上,它只是这样做,因为它在设置临时变量后没有清理。 我为它提出了一个问题 ,修复了它,运行了所有的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; } ]; 

注意 :该代码为checkContext指定null ,因为显然这是他们的样式。 如果是我,我已经分配了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(); }; }), 

问题出在以下函数中:在条件运算符中:

 function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); return !results.pop(); }; 

请注意,它永远不会清除input[0] 。 这是修复:

 function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); // Don't keep the element (issue #299) input[0] = null; return !results.pop(); }; 

这就是我现在有时间追踪的全部内容。

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

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