-
-
Save fearphage/159589 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| This gist aims to prove or disprove the assertion made here[1] and linked on | |
| Ajaxian[2] that dynamic script requests, or more specifically JSONP requests, | |
| leak memory in all modern browsers. | |
| The specific setup is a 200ms interval that requests an uncached script pointed at | |
| the simple php file. This file in response sends the following string: | |
| update() | |
| This is not technically a JSONP response, since the would-be callback is executed | |
| without arguments. However, memory footprint results can be gauged on generic | |
| script content rather than focused on the JSONP pattern. | |
| With all versions of this setup in the gist history, there IS a memory leak. | |
| Log: | |
| version 1: use a simple xbrowser getScript in the setup described | |
| version 2: move the script node clearing code out of the getScript function to test | |
| for closure related leakage. Add the "About this gist" file to track progress. | |
| version 3: formatting updates to "About this gist" file. | |
| version 4: testing setting defer to true (no change) | |
| version 5: wrapping node creation in try/finally to null node references (no change) | |
| version 6: calling script.clearAttributes() if available (IE) before node removal. Interestingly, this did seem to flatline the mem footprint until about 500 requests, at which point memory climbed rapidly. | |
| version 7: per Andrea Giammarchi's destroy suggestion[3][4], added while loop to remove | |
| all childNodes before removing from the DOM (no change) | |
| version 8: moved the clearAttributes and childNode removal after the script node | |
| removal. Again, seemed flat until ~400 requests, then shot up. | |
| version 9: replaced clearAttributes call with manual iteration of script props, | |
| assigning each to null inside a try/catch because IE throws errors. Memory | |
| consumption shot up faster than any prior configuration. | |
| 1. http://neil.fraser.name/news/2009/07/27/ | |
| 2. http://ajaxian.com/archives/dynamic-script-generation-and-memory-leaks | |
| 3. http://webreflection.blogspot.com/2009/04/drip-under-control-via-another-ie.html | |
| 4. http://ajaxian.com/archives/dynamic-script-generation-and-memory-leaks#comment-274727 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| header("Cache-control: no-cache"); | |
| header("Expires: -1"); | |
| header("Content-type: text/javascript"); | |
| echo "update()" | |
| ?> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!doctype html> | |
| <html> | |
| <head> | |
| <title>Test Page</title> | |
| </head> | |
| <body> | |
| <p id="inc">0</p> | |
| <p id="remaining">0</p> | |
| <button id="go">Start</button> | |
| <script type="text/javascript"> | |
| var timer = null, | |
| update_int = null, | |
| count = 0; | |
| // Executed by the requested scripts | |
| function update() { ++count; } | |
| function clearScript() { | |
| var hasReadyState = ('readyState' in this); | |
| if (!hasReadyState || /loaded|complete/.test(this.readyState)) { | |
| this[hasReadyState ? 'onreadystatechange' : 'onload'] = null; | |
| this.parentNode.removeChild(this); | |
| for (var k in this) { | |
| try { | |
| this[k] = null; | |
| } | |
| catch (e) {} | |
| } | |
| } | |
| } | |
| function getScript(U){ | |
| var h = document.documentElement.firstChild, | |
| s = document.createElement('script'); | |
| try { | |
| s[('readyState' in s) ? 'onreadystatechange' : 'onload'] = clearScript; | |
| s.defer = true; | |
| s.src = U; | |
| h.insertBefore(s,h.firstChild); | |
| } | |
| finally { | |
| s = h = null; | |
| } | |
| } | |
| document.getElementById('go').onclick = function () { | |
| if (timer) { | |
| clearInterval(timer); | |
| timer = null; | |
| update(); | |
| clearInterval(update_int); | |
| update_int = null; | |
| this.innerHTML = "Start"; | |
| } else { | |
| timer = setInterval(function () { | |
| getScript('script_leak.php?x='+(new Date().getTime())); | |
| },200); | |
| update_int = setInterval(function () { | |
| document.getElementById('inc').innerHTML = count; | |
| document.getElementById('remaining').innerHTML = document.getElementsByTagName('script').length; | |
| },1000); | |
| this.innerHTML = "Stop"; | |
| } | |
| }; | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment