同步AJAX调用如何导致内存泄漏
How synchronous AJAX call could cause memory leak?
我理解这个反对使用同步ajax调用的一般建议,因为同步调用阻塞了UI呈现。
通常给出的另一个原因是同步 AJAX的内存泄漏问题。
从MDN文档-
注意:你不应该使用同步xmlhttprequest,因为,由于网络固有的异步性质,有多种使用同步请求时内存和事件可能泄漏的方式。的唯一的例外是同步请求在worker中工作得很好。
同步调用如何导致内存泄漏?
我在找一个实际的例子。任何关于这个话题的文献都是很棒的。
如果每个规范都正确实现了XHR,那么它就不会泄漏:
如果XMLHttpRequest对象的状态为,则不能被垃圾收集已打开并且设置了send()标志,则其状态为HEADERS_RECEIVED或它的状态是LOADING,并且下列条件之一为真:
它有一个或多个注册的事件侦听器,其类型为Readystatechange、progress、abort、error、load、timeout或loadend
上传完成标志被取消设置,并且关联的XMLHttpRequestUpload对象注册了一个或多个事件侦听器类型为progress、abort、error、load、timeout、loadend。
如果XMLHttpRequest对象在其连接期间被垃圾收集仍然打开,则用户代理必须取消任何fetch实例该对象打开的算法,丢弃为它们排队的任何任务,并丢弃从网络接收到的任何进一步的数据。
因此,在您点击.send()
之后,XHR对象(以及它引用的任何东西)对GC免疫。但是,任何错误或成功都将使XHR进入DONE状态,并再次受到GC的约束。XHR对象是同步还是异步都无关紧要。在长同步请求的情况下,这并不重要,因为你只会被卡在send语句上,直到服务器响应。
然而,根据这张幻灯片,至少在2012年Chrome/Chromium中没有正确实现。根据规范,不需要调用.abort()
,因为DONE状态意味着XHR对象应该已经正常GCd了。
我找不到任何证据来支持MDN的声明,我已经通过twitter联系了作者。
我认为内存泄漏的发生主要是因为垃圾收集器不能完成它的工作。也就是说,你有一个对某些东西的引用,而GC不能删除它。我写了一个简单的例子:
var getDataSync = function(url) {
console.log("getDataSync");
var request = new XMLHttpRequest();
request.open('GET', url, false); // `false` makes the request synchronous
try {
request.send(null);
if(request.status === 200) {
return request.responseText;
} else {
return "";
}
} catch(e) {
console.log("!ERROR");
}
}
var getDataAsync = function(url, callback) {
console.log("getDataAsync");
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onload = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
callback(xhr.responseText);
} else {
callback("");
}
}
};
xhr.onerror = function (e) {
callback("");
};
xhr.send(null);
}
var requestsMade = 0
var requests = 1;
var url = "http://missing-url";
for(var i=0; i<requests; i++, requestsMade++) {
getDataSync(url);
// getDataAsync(url);
}
除了同步函数阻塞了很多东西之外,还有一个很大的区别。错误处理。如果您使用getDataSync并删除try-catch块并刷新页面,您将看到抛出错误。这是因为url不存在,但现在的问题是当抛出错误时垃圾收集器如何工作。是清除所有与错误相关的对象,还是保留错误对象之类的。如果有人知道更多,并写在这里,我会很高兴。
如果同步调用在完成之前被中断(即通过重用XMLHttpRequest对象的用户事件),那么未完成的网络查询可以被挂起,无法被垃圾收集。
这是因为,如果发起请求的对象在请求返回时不存在,则返回不能完成,但(如果浏览器不完善)保留在内存中。你可以很容易地使用setTimeout来删除请求对象,在请求发出之后,但在它返回之前。
我记得我在IE中遇到了一个大问题,回到2009年左右,但我希望现代浏览器不会受到它的影响。当然,现代库(即JQuery)防止了可能发生的情况,允许在不考虑它的情况下发出请求。
从GC同步XHR块线程的执行和该线程的函数执行堆栈中的所有对象。
例如:
function (b) {
var a = <big data>;
<work with> a and b
sync XHR
}
变量a和b在这里被阻塞(以及整个堆栈)。因此,如果GC开始工作,那么同步XHR阻塞了堆栈,所有堆栈变量将被标记为"幸存的GC",并从早期堆移到更持久的堆。一些对象即使在一次垃圾收集中也不应该存活,但它们会在多次垃圾收集中存活,甚至来自这些对象的引用也会在垃圾收集中存活。
About声明堆栈阻塞GC,并且该对象被标记为长活对象:请参阅努力回到精确。此外,在之后的"标记"对象GCed 通常堆被GCed,并且通常仅在仍然需要释放更多内存时才会(因为收集标记和清除的对象需要更多时间)。
更新:这真的是一个泄漏,而不仅仅是早期堆无效的解决方案吗?有几件事需要考虑。
- 请求完成后这些对象将被锁定多长时间?
- 同步XHR可以在无限的时间内阻塞堆栈,XHR没有超时属性(在所有非ie浏览器中),网络问题并不罕见。有多少UI元素被锁定?如果它在1秒内阻塞20M内存==在2分钟内领先200k。
- 考虑单个同步阻塞资源和浏览器音调的情况转到交换文件
- 当另一个事件试图改变DOM in可能被同步XHR阻塞时,另一个线程被阻塞(整个它的堆栈也是)
- 如果用户重复导致同步XHR的操作,整个浏览器窗口将被锁定。浏览器使用max=2线程来处理窗口事件。
- 即使没有阻塞,也会消耗大量的操作系统和浏览器内部资源:线程,临界区资源,UI资源,DOM…假设您可以(由于内存问题)在使用同步XHR的站点上打开10个选项卡,在使用异步XHR的站点上打开100个选项卡。不是这个内存泄漏。
使用同步AJAX请求的内存泄漏通常由以下原因引起:
- 使用setInterval/settimeout导致循环调用。
- XmlHttpRequest -当引用被删除时,所以xhr变得不可访问
当浏览器由于某种原因没有从不再需要的对象中释放内存时,就会发生内存泄漏。
这可能是由于浏览器错误,浏览器扩展问题,更罕见的是,我们在代码架构上的错误。
下面是一个在新上下文中运行setInterval时导致内存泄漏的示例:
var
Context = process.binding('evals').Context,
Script = process.binding('evals').Script,
total = 5000,
result = null;
process.nextTick(function memory() {
var mem = process.memoryUsage();
console.log('rss:', Math.round(((mem.rss/1024)/1024)) + "MB");
setTimeout(memory, 100);
});
console.log("STARTING");
process.nextTick(function run() {
var context = new Context();
context.setInterval = setInterval;
Script.runInContext('setInterval(function() {}, 0);',
context, 'test.js');
total--;
if (total) {
process.nextTick(run);
} else {
console.log("COMPLETE");
}
});
- 重复应用 d3 转换导致的内存泄漏
- IE7中的blockUI插件内存泄漏25kb
- Javascript闭包-如何防止内存泄漏
- jQuery Draggable:内存泄漏
- "检测到可能的EventEmitter内存泄漏”;使用Gulp+Watchify+Factor捆绑包
- 在Dojo类中递归调用setTimeout时是否存在内存泄漏
- 是内存泄漏
- 将处理程序留在img.onload上是内存泄漏
- 具有并发sse连接的node.js内存泄漏
- 简单对象的Javascript内存泄漏
- WeakMap是否会将我从父/子关系的内存泄漏中拯救出来
- Javascript绘制画布内存泄漏
- Node.js”;检测到EventEmitter内存泄漏”;
- 正在清理内存泄漏
- 递归Javascript对象是否会导致任何问题(内存泄漏)
- Angular JS$编译服务导致$watch内存泄漏
- 如何防止和防范闭包内存泄漏
- 跟踪 JavaScript 内存泄漏的工具
- 页面刷新后javascript内存泄漏有问题吗?为什么?
- XMLHttpRequest循环内存泄漏