我正在开发一个创建和删除大量DOM的应用程序。我注意到尽管javascript堆内存保持不变,但浏览器选项卡中的进程内存不断增加。在测试应用程序中,我从父div创建并删除div。
<button onclick="createStuff()">Create</button>
<button onclick="deleteStuff()">Delete</button>
<div id="parent"></div>
function createStuff() {
var parentDiv = document.getElementById('parent');
for (var i = 0; i < 50000; i++) {
var child = document.createElement('div');
child.id = i;
child.textContent = i;
parentDiv.appendChild(child);
child = null;
}
parentDiv = null;
}
function deleteStuff() {
var parentDiv = document.getElementById('parent');
for (var i = 0; i < 50000; i++) {
var child = document.getElementById(i);
parentDiv.removeChild(child);
child = null;
}
parentDiv = null;
}
我已经确认javascript堆没有使用chrome dev工具泄漏(我是他们的新手所以我可能错过了一些东西)。然而,该过程的记忆继续增加。从我读过的所有内容中,我怀疑移除的doms仍然在dom堆中。
其他帖子也说浏览器最终会释放分配给删除的doms的内存。在上面的jsfiddle示例中,我多次点击创建和删除。我的javascript堆稳定在4.9MB。我的进程内存高达115MB。我已经等了30分钟,而且根本没有下降。
问题
感谢您的帮助!
修改
我使用了chrome dev工具,javascript堆没有增长。有趣的是,堆快照之间唯一的变化是(数组)对象。我的理解是,括号中的任何内容都由浏览器控制,并且超出了我的范围。每个后续的create-&gt; delete都会删除旧的(数组)对象,并在删除过程中创建一个新对象。
在时间轴中,我可以看到javascript堆是常量并且节点被清理干净,但是(shift + esc)所示的内存即使在节点数量下降后也不会下降。
似乎我正在尽我所能确保清理我的javascript堆,但是dom清理是我无法实现的,并且独立于javascript GC。这个陈述是否正确?
移除的doms是否是年轻一代的一部分?有没有办法设置这个堆大小的限制?我重复测试,直到我达到500MB仍然没有清理。我正在使用Chrome 35.0.1916.114 btw。
答案 0 :(得分:1)
我知道你问过Chrome,但我会在Firefox中描述它是如何运作的,希望你和其他读者可能会感兴趣。 Chrome可能会有类似的工作,我不确定。
使用您的测试用例,Firefox内存使用量不会持续增加。只有在第一次创建/删除元素时,内存使用量才会永久增加。 在后续的创建/删除周期中,随后将回收所有已分配的内存。
至少在Firefox中,您无法在不重新加载页面的情况下强制释放此内存。如果你真的需要分配这么多内存暂时,你应该在iframe中执行此操作,以便在完成后丢弃。
技术细节如下:
在Firefox中,有一种工具来检查名为about:memory的内存使用情况。它按类别分解已用内存,并具有强制清理内存的控制(GC / CC /最小化内存)。
以下是about:memory
在创建/删除DOM元素后以及GC启动后的相关位:
├──34.34 MB (03.30%) -- window(http://fiddle.jshell.net/PSxPz/2/show/)
│ ├──26.54 MB (02.55%) -- layout
│ │ ├──13.95 MB (01.34%) -- (8 tiny)
│ │ │ ├───7.63 MB (00.73%) ── line-boxes
│ │ │ ├───4.00 MB (00.38%) ── pres-contexts
│ │ │ ├───2.26 MB (00.22%) ── pres-shell
│ │ │ ├───0.04 MB (00.00%) ── style-structs
│ │ │ ├───0.01 MB (00.00%) ── rule-nodes
│ │ │ ├───0.01 MB (00.00%) ── style-contexts
│ │ │ ├───0.00 MB (00.00%) ── style-sets
│ │ │ └───0.00 MB (00.00%) ── text-runs
│ │ └──12.59 MB (01.21%) -- frames
│ │ ├───7.25 MB (00.70%) ── nsBlockFrame
│ │ ├───5.34 MB (00.51%) ── nsTextFrame
│ │ └───0.00 MB (00.00%) ── sundries
│ └───7.80 MB (00.75%) -- (4 tiny)
│ ├──7.51 MB (00.72%) ++ dom
│ ├──0.29 MB (00.03%) ++ js-compartment(http://fiddle.jshell.net/PSxPz/2/show/)
│ ├──0.00 MB (00.00%) ── style-sheets
│ └──0.00 MB (00.00%) ── property-tables
(如果DOM节点已从文档中删除,但尚未收集垃圾,则会显示在orphan-nodes
测量下。)
大部分额外内存(创建DOM节点时请求)保留用于布局。
此行为is基于测量结果,显示真实网页在其生命周期中通常需要大约相同数量的布局对象:典型的网页不会仅分配10,000个帧销毁它们并显示一个非常简单的页面,就像这里的测试用例一样。
这种内存管理行为可以提高帧分配/解除分配的速度,减少memory fragmentation,并在帧被破坏后访问时避免令人讨厌的安全漏洞。
答案 1 :(得分:0)
浏览器应该照顾它。 113MB的内存仍然很低。
但是作为一个例子,请考虑这个: http://jsfiddle.net/gildean/PSxPz/3/
<button class="create">Create</button>
<button class="delete">Delete</button>
<div id="parent"></div>
<script>
var parentDiv = document.querySelector('#parent');
var actions = {
create: function createStuff() {
var frag = document.createDocumentFragment();
for (var i = 0; i < 10000; i++) {
var child = document.createElement('div');
child.id = i;
child.textContent = i;
frag.appendChild(child);
}
parentDiv.appendChild(frag);
},
delete: function deleteStuff() {
while (parentDiv.children.length) parentDiv.removeChild(parentDiv.firstChild);
}
};
Array.prototype.forEach.call(document.querySelectorAll('button'), function addListener(el) {
el.addEventListener('click', function handler(event) {
console.log(event.target.textContent + '!');
actions[event.target.className]();
});
});
</script>