我有一个相当复杂的Javascript应用程序,它有一个每秒调用60次的主循环。似乎有很多垃圾回收正在进行中(基于Chrome开发工具中内存时间轴的“锯齿”输出) - 这通常会影响应用程序的性能。
所以,我正在研究减少垃圾收集器必须做的工作量的最佳实践。(我在网上能找到的大部分信息都是关于避免内存泄漏的,这是一个稍微不同的问题——我的内存正在被释放,只是有太多的垃圾回收机制在进行。)我假设这主要归结为尽可能多地重用对象,但当然魔鬼在细节中。
该应用程序按照John Resig的Simple JavaScript继承的思路在“类”中构建。
我认为一个问题是,某些函数每秒可以调用数千次(因为它们在主循环的每次迭代中被使用数百次),也许这些函数中的局部工作变量(字符串,数组等)可能是问题所在。
我知道较大/较重对象的对象池(我们在一定程度上使用它),但是我正在寻找可以全面应用的技术,尤其是与在紧密循环中被多次调用的函数相关的技术。
我可以使用哪些技术来减少垃圾收集器必须做的工作量?
也许还有——什么技术可以用来识别哪些对象被垃圾收集得最多?(这是一个非常大的代码库,所以比较堆的快照并不是很有成效)
一般来说,你应该尽可能多的缓存,尽可能少的创建和销毁你的循环。
我首先想到的是在主循环中减少匿名函数的使用(如果有的话)。此外,很容易陷入创建和销毁传递给其他函数的对象的陷阱。我绝不是javascript专家,但我可以想象:
var options = {var1: value1, var2: value2, ChangingVariable: value3};
function loopfunc()
{
//do something
}
while(true)
{
$.each(listofthings, loopfunc);
options.ChangingVariable = newvalue;
someOtherFunction(options);
}
会比这运行得快得多:
while(true)
{
$.each(listofthings, function(){
//do something on the list
});
someOtherFunction({
var1: value1,
var2: value2,
ChangingVariable: newvalue
});
}
您的程序是否有任何停机时间?也许您需要它平稳运行一两秒钟(例如,对于动画),然后它有更多的时间来处理?如果是这种情况,我可以看到获取通常在整个动画过程中被垃圾回收的对象,并在某个全局对象中保留对它们的引用。然后,当动画结束时,您可以清除所有引用,并让垃圾回收器完成其工作。
抱歉,如果这与你已经尝试和想到的相比有点微不足道。
Chrome开发工具有一个很好的跟踪内存分配的功能。它被称为记忆时间线。本文描述了一些细节。我想这就是你所说的“锯齿”?这是大多数GC'ed运行时的正常行为。分配继续进行,直到达到使用阈值触发收集。通常在不同阈值下有不同类型的集合。
垃圾收集包括在与跟踪相关的事件列表中以及它们的持续时间。在我相当旧的笔记本上,短暂的收集发生在大约4Mb,需要30ms。这是60Hz循环迭代中的2次。如果这是一个动画,30ms的收集可能会导致口吃。您应该从这里开始查看您的环境中发生了什么:收集阈值在哪里以及收集需要多长时间。这为您评估优化提供了一个参考点。但是您可能不会比通过减慢分配率、延长集合之间的间隔来降低口吃频率更好。
下一步是使用配置文件|记录堆分配功能,用于按记录类型生成分配目录。这将快速显示哪些对象类型在跟踪期间消耗的内存最多,这相当于分配速率。按速率降序关注这些内容。
这些技术不是火箭科学。当你可以用一个未装箱的对象时,避免装箱对象。使用全局变量来保存和重用单个装箱对象,而不是在每次迭代中分配新的对象。在自由列表中汇集常见对象类型,而不是放弃它们。缓存字符串连接结果,这些结果可能在未来的迭代中重复使用。通过在封闭范围内设置变量来避免仅仅返回函数结果的分配。您必须在自己的上下文中考虑每种对象类型,以找到最佳策略。如果您需要有关细节的帮助,请发布编辑描述您正在查看的挑战的详细信息。
我建议不要在整个应用程序中歪曲您的正常编码风格,以尝试产生更少的垃圾。出于同样的原因,您不应该过早地优化速度。你的大部分努力加上代码增加的复杂性和晦涩难懂将毫无意义。
在大多数其他场景中,你需要做很多事情来最小化GC变动,这违背了习惯性的JS,所以在判断我给出的建议时,请记住上下文。
分配发生在现代口译员的几个地方:
new
或通过文字语法[…]
或{}
创建对象时。(function(…){…})
。Object(myNumber)
或Number.prototype.toString.call(42)
Array.prototype.slice
。参数
反映参数列表时。避免这样做,并尽可能汇集和重用对象。
具体来说,寻找机会:
拆分
或正则表达式匹配项重复解析,因为每个匹配项都需要多个对象分配。这经常发生在查找表和动态 DOM 节点 ID 中的键上。例如,查找表 ['foo-' x]
和文档.get 元素ById(“foo-'x)都
涉及分配,因为存在字符串串联。通常,您可以将密钥附加到长期存在的对象,而不是重新串联。根据您需要支持的浏览器,您可能能够使用 Map
直接将对象用作键。尝试 { op(x) } catch (e) { ... }
, do if (!opCouldFailOn(x)) { op(x); } else { ... }
.JSON.stringify
这样的内置内容,它使用内部本机缓冲区来累积内容,而不是分配多个对象。参数
,因为使用函数在调用时必须创建类似数组的对象。我建议使用< code>JSON.stringify来创建传出的网络消息。使用< code>JSON.parse解析输入消息显然涉及到分配,而且对于大消息来说需要大量的分配。如果您可以将传入的消息表示为原语数组,那么您可以节省大量分配。唯一可以用来构建不分配的解析器的内置代码是< code > string . prototype . charcode at 。一个复杂格式的解析器,它只使用了,但是读起来很糟糕。
问题内容: 我正在使用util模块调试nodejs应用程序,而 heapUsed 值保持在30-100MB左右,而 heapTotal 值增长到1.4GB。 我已经读到这是v8垃圾收集器的行为方式,但是问题是,例如在512 MB设备上运行时,如何减少其分配的内存量(使其小于1.4GB) 问题答案: 您需要控制最大内存大小标志(所有大小以MB为单位)。 对于“低内存设备”,建议的数量为: 适用于32
我正在做一个与JVM GC相关的项目,我计划用我的手动GC取代JVM自动GC。 我知道JAVA有一个自动垃圾收集器。如果我们集成一个新的手动垃圾收集器,其中开发人员需要显式地调用new并删除对象(如在C中)。 让我们假设程序员在没有内存泄漏的情况下写入空闲。 使用手动垃圾回收机制代替自动垃圾回收机制是否有效? 在工业中使用手动GC是否常见?还是程序员到处使用自动垃圾回收器?
Java 15 使 ZGC、Z 垃圾收集器成为标准功能。它是 Java 15 之前的一个实验性功能。它是低延迟、高度可扩展的垃圾收集器。 ZGC 是在 Java 11 中作为一项实验性功能引入的,因为开发人员社区认为它太大而无法提前发布。 即使在机器学习应用程序等海量数据应用程序的情况下,ZGC 也具有高性能和高效工作。它确保在处理数据时不会因垃圾收集而长时间停顿。它支持 Linux、Window
Java 15 使 ZGC、Z 垃圾收集器成为标准功能。它是 Java 15 之前的一个实验性功能。它是低延迟、高度可扩展的垃圾收集器。 ZGC 是在 Java 11 中作为一项实验性功能引入的,因为开发人员社区认为它太大而无法提前发布。从那时起,对这个垃圾收集做了很多改进,例如 - 并发类卸载 取消提交未使用的内存 支持班级数据共享 NUMA 多线程堆Pre-touch 最大堆大小限制从 4 T
问题内容: 我从带有node.js的线程垃圾收集中学到了node.js使用世代GC。 我通常使用循环对象引用(最终我都会删除/确保超出范围),并想知道node.js是否能很好地处理它们。所以例如。如果使用参考完成。计数,会有一个问题,所以我想知道这个节点有多好。 一些使用场景: 对于每个http请求,我创建一个带有lambda的setTimeout,该lambda可能引用了范围对象。作用域对象还引
Kubernetes 垃圾收集器的角色是删除指定的对象,这些对象曾经有但以后不再拥有 Owner 了。 注意:垃圾收集是 beta 特性,在 Kubernetes 1.4 及以上版本默认启用。 Owner 和 Dependent 一些 Kubernetes 对象是其它一些的 Owner。例如,一个 ReplicaSet 是一组 Pod 的 Owner。具有 Owner 的对象被称为是 Owner