最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - jQuerySizzle checkContext memory leak - Stack Overflow

programmeradmin2浏览0评论

While debugging my app with 'Profiles' in DevTools I found "Detached DOM tree's" accumulating. These detached nodes have retaining tree consisting mostly of checkContext functions (ing from sizzle inside jQuery - v1.10.1).

I'm not sure how to proceed with this. What does this result mean?

While debugging my app with 'Profiles' in DevTools I found "Detached DOM tree's" accumulating. These detached nodes have retaining tree consisting mostly of checkContext functions (ing from sizzle inside jQuery - v1.10.1).

I'm not sure how to proceed with this. What does this result mean?

Share Improve this question asked Jun 24, 2013 at 15:23 Konrad DzwinelKonrad Dzwinel 37.9k12 gold badges102 silver badges106 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

Sizzle stores piled selectors in selector cache, which by default stores up to 50 entries. You may experiment by setting $.expr.cacheLength = 1 before doing any selections and see if they go away.

Here's the docs https://github./jquery/sizzle/wiki/Sizzle-Documentation#-internal-api. Seems internal so don't depend on it or anything in actual production code.

This is actually a bug, there's no reason that Sizzle needs to hang onto the context node, it's only doing it because it's not cleaning up after setting a temporary variable. I filed an issue for it, fixed it, ran all the Sizzle tests, and did a pull request.

If you want to patch your existing copy of jQuery or Sizzle:

  1. Open your jQuery or Sizzle file

  2. Search for the matcherFromTokens function

  3. Find this code within it (near the top):

    matchers = [ function( elem, context, xml ) {
        return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
            (checkContext = context).nodeType ?
                matchContext( elem, context, xml ) :
                matchAnyContext( elem, context, xml ) );
    } ];
    
  4. Change the return to var rv =, and add checkContext = undefined; and then return rv; at the end of the anonymous function, e.g.:

    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;
    } ];
    

Note: That code assigns null to checkContext because apparently that's their style. If it were me, I'd've assigned undefined instead.

If there's any issue with the fix raised during the pull request / merge process, I'll update the answer.

It's better to keep letting Sizzle cache selectors, because jQuery uses the piled selector stuff with event delegation, and you don't really want it to have to reparse and rebuild the matcher functions each time a relevant event occurs so it can figure out whether elements match it.


This isn't the only place jQuery holds onto elements in piled selectors, unfortunately. Each place it does is probably a bug that could use fixing. I've only had the time to track down one other, which I've also reported and fixed (pending the pull request being landed):

If you search for "Potentially plex pseudos" you'll find this for the :not pseudo-selector:

pseudos: {
    // Potentially plex pseudos
    "not": markFunction(function( selector ) {
        // Trim the selector passed to pile
        // to avoid treating leading and trailing
        // spaces as binators
        var input = [],
            results = [],
            matcher = pile( 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();
            };
    }),

The problem is in the function after the : in the conditional operator:

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

Note that it never clears input[0]. Here's the fix:

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

That's all I have the time to track down at present.

发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>