这篇文章的作者(我们今天将其翻译发表)谈论了如何监视分配给网页的内存。认真注意生产中工作的页面的内存有助于将Web项目的生产率保持在较高水平。
浏览器自动控制分配给网页的内存。页面创建对象时,浏览器会使用其内部机制分配内存以存储该对象。由于内存不是无限资源,因此浏览器会定期执行垃圾回收,在此期间,将检测到不必要的对象并清除它们占用的内存。但是,检测此类物体的过程并不理想。已经证明绝对准确,完整地识别此类物体是一项艰巨的任务。结果,浏览器将查找``不必要的对象''的想法替换为查找``无法访问的对象''的想法。如果网页无法通过其变量和可访问的其他对象的字段访问该对象,则意味着浏览器可以安全地清除该对象占用的内存。如以下示例所示,“不必要”和“无法访问”之间的差异导致内存泄漏:const object = { a: new Array(1000), b: new Array(2000) };
setInterval(() => console.log(object.a), 1000);
这里有一个很大的不必要数组b
,但是浏览器不会释放它占用的内存,因为它可以通过object.b
回调中的object属性访问。结果,此阵列占用的内存正在泄漏。内存泄漏是Web开发中的常见现象。例如,当开发人员忘记取消订阅事件侦听器,意外捕获元素中的对象,iframe
忘记关闭工作器,收集数组中的对象时,它们很容易出现在程序中。如果网页内存泄漏,将导致页面内存消耗随着时间的流逝而增加。这样的页面对于用户而言似乎越来越慢。解决此问题的第一步是进行测量。新的performance.measureMemory() API 允许开发人员通过生产中的网页来衡量内存使用情况,从而检测通过本地测试而漏出的内存。新的performance.measureMemory()API与旧的performance.memory有何不同?
如果您熟悉现有的非标准API performance.memory
,那么您可能会对新API与旧API有何不同的问题感兴趣。主要区别在于,旧的API返回JavaScript堆的大小,而新的API将评估整个网页的内存使用情况。当Chrome组织多个网页(或同一页面的多个实例)之间的堆共享时,这一区别非常重要。在这种情况下,旧API返回的结果可能会失真。由于旧的API是用特定于实现的术语(例如堆)定义的,因此标准化它是没有希望的事情。另一个区别是在Chrome中,新API在收集垃圾时会进行内存测量。这样可以减少测量结果中的“噪音”,但是可能需要一些时间才能获得结果。请注意,其他浏览器的创建者可能会决定实施新的API,而不会绑定到垃圾回收。使用新API的推荐方法
网页对内存的使用取决于事件的发生,用户操作和垃圾回收。因此,API performance.measureMemory()
旨在研究生产中的内存使用级别。在测试环境中调用此API的结果不太有用。以下是使用它的选项示例:根据Origin Trial方案,当前仅在Chrome 83中支持该API。API返回的结果高度依赖于实现,因为不同的浏览器使用不同的方式来表示内存中的对象,并且使用不同的方式来评估内存使用级别。如果对所有已用内存的完整记帐是一项不合理的困难或不可能的任务,则浏览器可以将某些区域的内存从记帐中排除。结果,我们可以说此API在不同浏览器中产生的结果不可比。仅比较在同一浏览器中获得的结果是有意义的。目前的工作进度
使用performance.measureMemory()
▍启用原产地试用支持
performance.measureMemory()
Chrome 83可以在Origin Trial方案下使用
该API 。此阶段预计将随着Chrome 84的发布而结束。OriginTrial使开发人员可以利用Chrome的新功能,并与网络社区分享有关这些功能的便利性,可用性和有效性的反馈。有关此程序的详细信息,请参见此处。您可以在注册页面上订阅该程序。Origin原产地注册程序中的注册
- 请求令牌以获取您感兴趣的机会。
- 将令牌添加到试验项目的页面。有两种方法可以做到这一点:
- 在每个页面的标题上添加
<meta>
标签origin-trial
。例如,它可能看起来像这样:<meta http-equiv="origin-trial" content="TOKEN_GOES_HERE">
。 - 如果您有权访问服务器设置,则可以使用HTTP标头添加令牌
Origin-Trial
。结果,响应标头应如下所示:Origin-Trial: TOKEN_GOES_HERE
。
through通过Chrome标志启用新功能
为了进行实验performance.measureMemory()
,同时与原产地标记试验配药,您需要启用该标志#experimental-web-platform-features
在chrome://flags
。API检查API使用
函数调用performance.measureMemory()
可能会失败,并抛出SecurityError。如果环境不满足信息泄漏的安全要求,则会发生这种情况。在Chrome中进行原产地测试时,此API需要包含“ 网站隔离”。当API可以正常使用时,它将依赖crossOriginIsolated属性。通过设置标头COOP和COEP,网页可以在此模式下运行。这是一个示例代码:if (performance.measureMemory) {
let result;
try {
result = await performance.measureMemory();
} catch (error) {
if (error instanceof DOMException &&
error.name === "SecurityError") {
console.log("The context is not secure.");
} else {
throw error;
}
}
console.log(result);
}
▍本地测试
Chrome会在收集垃圾时进行内存测量。这意味着访问API不会立即解决承诺。为了获得结果,您需要等待下一个垃圾回收会话。该API会在特定时间(当前设置为20秒)后强制启动垃圾收集。如果您使用命令行标志运行Chrome --enable-blink-features='ForceEagerMeasureMemory'
,则超时将减少为零,这对于本地调试和本地测试很有用。例
建议您通过定义一个全局内存监视器来使用新的API,该监视器测量整个页面的内存使用级别并将结果发送到服务器,在此可以汇总和分析结果。使用此API的最简单方法是进行定期测量。例如,它们可以每M
分钟运行一次。但是,这会导致数据失真,因为在两次测量之间可能会出现内存使用量的峰值。以下示例演示了如何使用泊松过程进行无系统误差的测量。这种方法可确保测量会话可以在任何时间点均等地发生(这是该方法的演示,这是源代码)。首先,声明一个函数,该函数setTimeout()
使用具有随机设置的时间间隔的函数来计划会话的下一次启动以测量已消耗的内存量。在浏览器窗口中加载页面后,应调用此函数。function scheduleMeasurement() {
if (!performance.measureMemory) {
console.log("performance.measureMemory() is not available.");
return;
}
const interval = measurementInterval();
console.log("Scheduling memory measurement in " +
Math.round(interval / 1000) + " seconds.");
setTimeout(performMeasurement, interval);
}
window.onload = function () {
scheduleMeasurement();
}
该函数measurementInterval()
找到一个以毫秒为单位表示的随机间隔,该间隔设置为大约每五分钟执行一次测量。如果您对该函数所基于的数学概念感兴趣,请阅读有关指数分布的信息。function measurementInterval() {
const MEAN_INTERVAL_IN_MS = 5 * 60 * 1000;
return -Math.log(Math.random()) * MEAN_INTERVAL_IN_MS;
}
结果,异步函数performMeasurement()
调用了我们的API,记录了结果并计划了下一次测量。async function performMeasurement() {
let result;
try {
result = await performance.measureMemory();
} catch (error) {
if (error instanceof DOMException &&
error.name === "SecurityError") {
console.log("The context is not secure.");
return;
}
throw error;
}
console.log("Memory usage:", result);
scheduleMeasurement();
}
测量结果可能如下所示:
{
bytes: 60_000_000,
breakdown: [
{
bytes: 40_000_000,
attribution: ["https://foo.com"],
userAgentSpecificTypes: ["Window", "JS"]
},
{
bytes: 20_000_000,
attribution: ["https://foo.com/iframe"],
userAgentSpecificTypes: ["Window", "JS"]
}
]
}
在该字段中显示内存使用总体水平的估计值bytes
。在得出此估计时,使用数字的分隔符。这些值高度依赖于实现。如果收到的是不同的浏览器,则无法进行比较。即使在同一浏览器的不同版本中,获取它们的方式也可能有所不同。当Origin Trial程序运行时,返回值包括主窗口的JavaScript内存使用情况指示符,iframe
同一站点中元素的内存使用情况指示符以及相关窗口的指示符。当API准备就绪时,此值将表示有关JavaScript,DOM,iframe
与Windows和Web Worker关联的所有元素消耗的内存的信息。清单breakdown
提供有关已用内存的更多详细信息。每个条目描述一块内存,并将此片段与iframe
URL标识的一组窗口,元素或工作程序相关联。该字段userAgentSpecificTypes
列出了由实现功能确定的内存类型。重要的是要以一般的方式考虑这些列表,而不要依靠某个浏览器的功能尝试根据这些列表来分析所有内容。例如,某些浏览器可能返回空列表breakdown
或空字段attribution
。其他浏览器可能在一个元素中返回attribution
多个URL ,这表明它们无法查明内存属于这些URL中的哪个。反馈
Web Performance Community Group和Chrome开发团队将很高兴发现您的想法performance.measureMemory()
并了解使用此API的经验。与我们分享您对API设备的想法
此API中是否有某些功能无法正常使用?也许缺少实现您的想法所需的东西?在项目跟踪器中打开一个新任务,或对现有任务进行注释。报告实施问题
在您的Chrome实施中发现错误?还是事实证明实现与规范不同?在此处记录错误:new.crbug.com。尝试在您的消息中包含尽可能多的细节,包括有关如何重现该错误的简单说明,并指出问题与有关Blink>PerformanceAPIs
。小故障是演示错误的一种很好的方法。支持我们
performance.measureMemory()
您
打算使用吗?如果是这样,请告诉我们。这些故事可帮助Chrome开发团队确定优先级。这些故事向其他浏览器的创建者展示了支持新功能的重要性。如果需要,请向@ChromiumDev发送一条推文,并告诉我们有关在何处以及如何使用新API的信息。亲爱的读者们!你试过了performance.measureMemory()
吗?